From 1a04b5c61ece78151a5fee955b56b8988ccb5876 Mon Sep 17 00:00:00 2001 From: larry Date: Sun, 6 Mar 2016 01:31:40 +0900 Subject: [PATCH] Site updated at 2016-03-05 16:31:40 UTC --- 1/index.html | 8 +- 10/index.html | 8 +- 11/index.html | 8 +- 12/index.html | 8 +- 13/index.html | 8 +- 14/index.html | 8 +- 15/index.html | 8 +- 16/index.html | 8 +- 17/index.html | 8 +- 18/index.html | 8 +- 19/index.html | 8 +- 2/index.html | 8 +- 20/index.html | 8 +- 21/index.html | 8 +- 22/index.html | 8 +- 23/index.html | 8 +- 24/index.html | 8 +- 25/index.html | 8 +- 26/index.html | 8 +- 27/index.html | 8 +- 28/index.html | 8 +- 29/index.html | 8 +- 3/index.html | 8 +- 30/index.html | 8 +- 31/index.html | 8 +- 32/index.html | 8 +- 33/index.html | 8 +- 34/index.html | 8 +- 35/index.html | 8 +- 36/index.html | 8 +- 37/index.html | 8 +- 38/index.html | 8 +- 39/index.html | 8 +- 4/index.html | 8 +- 40/index.html | 8 +- 42/index.html | 8 +- 43/index.html | 8 +- 44/index.html | 8 +- 45/index.html | 8 +- 47/index.html | 8 +- 48/index.html | 8 +- 49/index.html | 8 +- 5/index.html | 8 +- 50/index.html | 8 +- 51/index.html | 8 +- 52/index.html | 8 +- 54/index.html | 8 +- 55/index.html | 8 +- 57/index.html | 8 +- 58/index.html | 8 +- 59/index.html | 8 +- 6/index.html | 8 +- 60/index.html | 8 +- 61/index.html | 8 +- 62/index.html | 8 +- 63/index.html | 8 +- 64/index.html | 8 +- 69/index.html | 8 +- 7/index.html | 8 +- 70/index.html | 8 +- 72/index.html | 8 +- 73/index.html | 46 +- 74/index.html | 8 +- 75/index.html | 8 +- 76/index.html | 8 +- 77/index.html | 8 +- 79/index.html | 8 +- 8/index.html | 8 +- 80/index.html | 8 +- 81/index.html | 8 +- 82/index.html | 8 +- 85/index.html | 8 +- 86/index.html | 8 +- 87/index.html | 8 +- 88/index.html | 8 +- 89/index.html | 8 +- 9/index.html | 8 +- 90/index.html | 8 +- 91/index.html | 8 +- 92/index.html | 8 +- 93/index.html | 8 +- 95/index.html | 603 ++++++++++++++++++ archives/index.html | 21 +- atom.xml | 331 ++++++++-- blog/categories/big-data/atom.xml | 2 +- blog/categories/big2014/atom.xml | 2 +- blog/categories/book/atom.xml | 2 +- blog/categories/bootstrap/atom.xml | 2 +- blog/categories/common/atom.xml | 2 +- blog/categories/conference/atom.xml | 2 +- blog/categories/cousera-nn/atom.xml | 2 +- blog/categories/deep-learning/atom.xml | 2 +- blog/categories/development/atom.xml | 2 +- blog/categories/facebook/atom.xml | 2 +- blog/categories/game/atom.xml | 2 +- blog/categories/gitlab/atom.xml | 2 +- blog/categories/icml/atom.xml | 2 +- blog/categories/icml2014/atom.xml | 2 +- blog/categories/jounals/atom.xml | 2 +- blog/categories/kaggle/atom.xml | 2 +- blog/categories/kaist/atom.xml | 2 +- blog/categories/lecture/atom.xml | 2 +- .../machine-learning-study/atom.xml | 482 ++++++++------ .../machine-learning-study/index.html | 15 + blog/categories/machine-learning/atom.xml | 364 ++++++++--- blog/categories/machine-learning/index.html | 15 + blog/categories/math-in-internet/atom.xml | 2 +- blog/categories/mobile/atom.xml | 2 +- blog/categories/modeling/atom.xml | 2 +- blog/categories/movie/atom.xml | 2 +- blog/categories/music/atom.xml | 2 +- blog/categories/network-science/atom.xml | 2 +- blog/categories/network/atom.xml | 2 +- blog/categories/neural-network/atom.xml | 2 +- blog/categories/octopress/atom.xml | 2 +- blog/categories/paper-review/atom.xml | 2 +- blog/categories/platform/atom.xml | 2 +- .../reinforcement-learning/atom.xml | 2 +- blog/categories/research/atom.xml | 2 +- blog/categories/seminar/atom.xml | 2 +- blog/page/2/index.html | 32 +- blog/page/3/index.html | 33 +- blog/page/4/index.html | 33 +- blog/page/5/index.html | 30 +- blog/page/6/index.html | 32 +- blog/page/7/index.html | 35 +- blog/page/8/index.html | 33 +- blog/page/9/index.html | 19 +- index.html | 32 +- sitemap.xml | 12 +- stylesheets/screen.css | 2 +- 131 files changed, 2059 insertions(+), 815 deletions(-) create mode 100644 95/index.html diff --git a/1/index.html b/1/index.html index 14b07b02..7d9135ee 100644 --- a/1/index.html +++ b/1/index.html @@ -228,6 +228,10 @@

Comments

Recent Posts

diff --git a/10/index.html b/10/index.html index ddb8a88f..cdf194b4 100644 --- a/10/index.html +++ b/10/index.html @@ -232,6 +232,10 @@

Comments

Recent Posts

diff --git a/11/index.html b/11/index.html index a75f989a..84ffff42 100644 --- a/11/index.html +++ b/11/index.html @@ -226,6 +226,10 @@

Comments

Recent Posts

diff --git a/12/index.html b/12/index.html index 132b2132..6a79fedb 100644 --- a/12/index.html +++ b/12/index.html @@ -226,6 +226,10 @@

Comments

Recent Posts

diff --git a/13/index.html b/13/index.html index e0136f9d..481909d2 100644 --- a/13/index.html +++ b/13/index.html @@ -223,6 +223,10 @@

Comments

Recent Posts

diff --git a/14/index.html b/14/index.html index f9e5ca42..67c57ee7 100644 --- a/14/index.html +++ b/14/index.html @@ -329,6 +329,10 @@

Comments

Recent Posts

diff --git a/15/index.html b/15/index.html index e8f14f32..ee9d1b9c 100644 --- a/15/index.html +++ b/15/index.html @@ -255,6 +255,10 @@

Comments

Recent Posts

diff --git a/16/index.html b/16/index.html index 7e4ff345..1938ee45 100644 --- a/16/index.html +++ b/16/index.html @@ -249,6 +249,10 @@

Comments

Recent Posts

diff --git a/17/index.html b/17/index.html index 7891d86b..010a2f76 100644 --- a/17/index.html +++ b/17/index.html @@ -238,6 +238,10 @@

Comments

Recent Posts

diff --git a/18/index.html b/18/index.html index 93f3a558..0e3532df 100644 --- a/18/index.html +++ b/18/index.html @@ -278,6 +278,10 @@

Comments

Recent Posts

diff --git a/19/index.html b/19/index.html index ac26557e..5b4a2550 100644 --- a/19/index.html +++ b/19/index.html @@ -226,6 +226,10 @@

Comments

Recent Posts

diff --git a/2/index.html b/2/index.html index 9c67cee2..ab69edd0 100644 --- a/2/index.html +++ b/2/index.html @@ -325,6 +325,10 @@

Comments

Recent Posts

diff --git a/20/index.html b/20/index.html index 4c1221e1..549324cf 100644 --- a/20/index.html +++ b/20/index.html @@ -232,6 +232,10 @@

Comments

Recent Posts

diff --git a/21/index.html b/21/index.html index e9a6cce6..088ef09a 100644 --- a/21/index.html +++ b/21/index.html @@ -275,6 +275,10 @@

Comments

Recent Posts

diff --git a/22/index.html b/22/index.html index 3fc58930..67c8d96b 100644 --- a/22/index.html +++ b/22/index.html @@ -235,6 +235,10 @@

Comments

Recent Posts

diff --git a/23/index.html b/23/index.html index 6abdcd0c..196b732d 100644 --- a/23/index.html +++ b/23/index.html @@ -251,6 +251,10 @@

Comments

Recent Posts

diff --git a/24/index.html b/24/index.html index 200e4210..b6efc694 100644 --- a/24/index.html +++ b/24/index.html @@ -238,6 +238,10 @@

Comments

Recent Posts

diff --git a/25/index.html b/25/index.html index 146ca61e..429f526a 100644 --- a/25/index.html +++ b/25/index.html @@ -246,6 +246,10 @@

Comments

Recent Posts

diff --git a/26/index.html b/26/index.html index b53bfef9..e7fd3137 100644 --- a/26/index.html +++ b/26/index.html @@ -248,6 +248,10 @@

Comments

Recent Posts

diff --git a/27/index.html b/27/index.html index d0674297..5df0b900 100644 --- a/27/index.html +++ b/27/index.html @@ -284,6 +284,10 @@

Comments

Recent Posts

diff --git a/28/index.html b/28/index.html index de67f31a..ac6c8ea1 100644 --- a/28/index.html +++ b/28/index.html @@ -256,6 +256,10 @@

Comments

Recent Posts

diff --git a/29/index.html b/29/index.html index 28fcb612..68ddbf4a 100644 --- a/29/index.html +++ b/29/index.html @@ -286,6 +286,10 @@

Comments

Recent Posts

diff --git a/3/index.html b/3/index.html index 473fa08c..f49bf196 100644 --- a/3/index.html +++ b/3/index.html @@ -263,6 +263,10 @@

Comments

Recent Posts

diff --git a/30/index.html b/30/index.html index 07b7a555..ca03a8cb 100644 --- a/30/index.html +++ b/30/index.html @@ -292,6 +292,10 @@

Comments

Recent Posts

diff --git a/31/index.html b/31/index.html index 93bd45b6..c9e76268 100644 --- a/31/index.html +++ b/31/index.html @@ -337,6 +337,10 @@

Comments

Recent Posts

diff --git a/32/index.html b/32/index.html index 905f25df..8a1aefeb 100644 --- a/32/index.html +++ b/32/index.html @@ -280,6 +280,10 @@

Comments

Recent Posts

diff --git a/33/index.html b/33/index.html index b62842e7..653cbc8c 100644 --- a/33/index.html +++ b/33/index.html @@ -271,6 +271,10 @@

Comments

Recent Posts

diff --git a/34/index.html b/34/index.html index a71ad1c6..6c09b2b6 100644 --- a/34/index.html +++ b/34/index.html @@ -302,6 +302,10 @@

Comments

Recent Posts

diff --git a/35/index.html b/35/index.html index fa7f306b..1cebd7c6 100644 --- a/35/index.html +++ b/35/index.html @@ -246,6 +246,10 @@

Comments

Recent Posts

diff --git a/36/index.html b/36/index.html index 343c3c78..8938ade9 100644 --- a/36/index.html +++ b/36/index.html @@ -387,6 +387,10 @@

Comments

Recent Posts

diff --git a/37/index.html b/37/index.html index 85cd6d41..d9c4f190 100644 --- a/37/index.html +++ b/37/index.html @@ -275,6 +275,10 @@

Comments

Recent Posts

diff --git a/38/index.html b/38/index.html index 767470a2..2aa7095e 100644 --- a/38/index.html +++ b/38/index.html @@ -351,6 +351,10 @@

Comments

Recent Posts

diff --git a/39/index.html b/39/index.html index 1e6f85ba..d4f614e8 100644 --- a/39/index.html +++ b/39/index.html @@ -318,6 +318,10 @@

Comments

Recent Posts

diff --git a/4/index.html b/4/index.html index 35604a88..565fd1ca 100644 --- a/4/index.html +++ b/4/index.html @@ -239,6 +239,10 @@

Comments

Recent Posts

diff --git a/40/index.html b/40/index.html index 56c4670f..acd6d82b 100644 --- a/40/index.html +++ b/40/index.html @@ -373,6 +373,10 @@

Comments

Recent Posts

diff --git a/42/index.html b/42/index.html index 1107df84..86d73556 100644 --- a/42/index.html +++ b/42/index.html @@ -357,6 +357,10 @@

Comments

Recent Posts

diff --git a/43/index.html b/43/index.html index 9964ee3a..851c40ef 100644 --- a/43/index.html +++ b/43/index.html @@ -272,6 +272,10 @@

Comments

Recent Posts

diff --git a/44/index.html b/44/index.html index 09e1532d..6095de8c 100644 --- a/44/index.html +++ b/44/index.html @@ -449,6 +449,10 @@

Comments

Recent Posts

diff --git a/45/index.html b/45/index.html index e76b218a..78f42dca 100644 --- a/45/index.html +++ b/45/index.html @@ -394,6 +394,10 @@

Comments

Recent Posts

diff --git a/47/index.html b/47/index.html index 739c01d6..b7f850b5 100644 --- a/47/index.html +++ b/47/index.html @@ -247,6 +247,10 @@

Comments

Recent Posts

diff --git a/48/index.html b/48/index.html index 0b8a45b3..369770cc 100644 --- a/48/index.html +++ b/48/index.html @@ -386,6 +386,10 @@

Comments

Recent Posts

diff --git a/49/index.html b/49/index.html index 308978f9..5d6212bf 100644 --- a/49/index.html +++ b/49/index.html @@ -402,6 +402,10 @@

Comments

Recent Posts

diff --git a/5/index.html b/5/index.html index 0c3e32f8..072c0c84 100644 --- a/5/index.html +++ b/5/index.html @@ -267,6 +267,10 @@

Comments

Recent Posts

diff --git a/50/index.html b/50/index.html index 902e89f8..8c85ead3 100644 --- a/50/index.html +++ b/50/index.html @@ -413,6 +413,10 @@

Comments

Recent Posts

diff --git a/51/index.html b/51/index.html index 89216809..0ff30e4e 100644 --- a/51/index.html +++ b/51/index.html @@ -405,6 +405,10 @@

Comments

Recent Posts

diff --git a/52/index.html b/52/index.html index 9b47bb2b..6a18b046 100644 --- a/52/index.html +++ b/52/index.html @@ -289,6 +289,10 @@

Comments

Recent Posts

diff --git a/54/index.html b/54/index.html index 0af26bc9..7cbfedf2 100644 --- a/54/index.html +++ b/54/index.html @@ -427,6 +427,10 @@

Comments

Recent Posts

diff --git a/55/index.html b/55/index.html index ffee1d06..7db0d0bd 100644 --- a/55/index.html +++ b/55/index.html @@ -250,6 +250,10 @@

Comments

Recent Posts

diff --git a/57/index.html b/57/index.html index 7b1d24ff..7164d99c 100644 --- a/57/index.html +++ b/57/index.html @@ -363,6 +363,10 @@

Comments

Recent Posts

diff --git a/58/index.html b/58/index.html index 30d95ded..d88cdfb9 100644 --- a/58/index.html +++ b/58/index.html @@ -357,6 +357,10 @@

Comments

Recent Posts

diff --git a/59/index.html b/59/index.html index e87c8df8..52960c46 100644 --- a/59/index.html +++ b/59/index.html @@ -352,6 +352,10 @@

Comments

Recent Posts

diff --git a/6/index.html b/6/index.html index e33dcfde..a0bf4f62 100644 --- a/6/index.html +++ b/6/index.html @@ -244,6 +244,10 @@

Comments

Recent Posts

diff --git a/60/index.html b/60/index.html index 2adb7dd9..332cc8e0 100644 --- a/60/index.html +++ b/60/index.html @@ -310,6 +310,10 @@

Comments

Recent Posts

diff --git a/61/index.html b/61/index.html index 9550844e..ffe7ac35 100644 --- a/61/index.html +++ b/61/index.html @@ -405,6 +405,10 @@

Comments

Recent Posts

diff --git a/62/index.html b/62/index.html index dba7e1e0..848351d4 100644 --- a/62/index.html +++ b/62/index.html @@ -371,6 +371,10 @@

Comments

Recent Posts

diff --git a/63/index.html b/63/index.html index bf607290..4384ede3 100644 --- a/63/index.html +++ b/63/index.html @@ -517,6 +517,10 @@

Comments

Recent Posts

diff --git a/64/index.html b/64/index.html index 7a7ded68..6b065154 100644 --- a/64/index.html +++ b/64/index.html @@ -343,6 +343,10 @@

Comments

Recent Posts

diff --git a/69/index.html b/69/index.html index 89f82130..9912b0f2 100644 --- a/69/index.html +++ b/69/index.html @@ -375,6 +375,10 @@

Comments

Recent Posts

diff --git a/7/index.html b/7/index.html index 10bc7108..3f8a90f4 100644 --- a/7/index.html +++ b/7/index.html @@ -279,6 +279,10 @@

Comments

Recent Posts

diff --git a/70/index.html b/70/index.html index edcc043b..13d91364 100644 --- a/70/index.html +++ b/70/index.html @@ -339,6 +339,10 @@

Comments

Recent Posts

diff --git a/72/index.html b/72/index.html index b7183fbe..91fd9dca 100644 --- a/72/index.html +++ b/72/index.html @@ -392,6 +392,10 @@

Comments

Recent Posts

diff --git a/73/index.html b/73/index.html index 08f7fbaf..aa549867 100644 --- a/73/index.html +++ b/73/index.html @@ -125,7 +125,7 @@
Recommendation Problem
Matrix Completion
-

그러면 이제 추천 문제를 보다 엄밀하게 정의해보도록 하자. 먼저 데이터에 대해 살펴보도록 하자. 이 문제에서는 사용자와 상품이라는 두 가지 요소들이 존재한다. 사용자 \(u\)가 아이템 \(i\)를 얼마나 좋아할 것인지 나타내는 값을 rating \(r_{ui}\)라 하자. 이때, 이 rating은 Netflix처럼 1에서 5 사이의 real value일수도 있으며, 아마존이나 페이스북처럼 클릭했는지 하지 않았는지에 대한 데이터일수도 있다. 앞선 경우는 사용자가 자신이 얼마나 이 아이템을 좋아하는지 ‘명시적으로’ 나타냈기 때문에 explicit feedback이라 부르며, 후자의 경우는 사용자가 해당 상품을 좋아했는지 싫어했는지 표현을 직접적으로 하지 않으므로 ‘implicit feedback’이라고 부른다. 이에 대해서는 나중에 다른 글을 통해 더 자세히 다루도록 하겠다. 지금은 \(r_{ui}\)의 정확한 값을 알고 있고, 이 값이 전혀 noise가 없는 값이라고 가정하고 문제를 계속 설명하도록 하겠다. 이런 경우 우리가 가지고 있는 데이터는 \(r_{ui}\)들의 값이 될 것이고, 대략 아래와 같은 방식으로 matrix로 표현할 수 있을 것이다.

+

그러면 이제 추천 문제를 보다 엄밀하게 정의해보도록 하자. 먼저 데이터에 대해 살펴보도록 하자. 이 문제에서는 사용자와 상품이라는 두 가지 요소들이 존재한다. 사용자 \(u\)가 아이템 \(i\)를 얼마나 좋아할 것인지 나타내는 값을 rating \(r_{ui}\)라 하자. 이때, 이 rating은 Netflix처럼 1에서 5 사이의 real value일수도 있으며, 아마존이나 페이스북처럼 클릭했는지 하지 않았는지에 대한 데이터일수도 있다. 앞선 경우는 사용자가 자신이 얼마나 이 아이템을 좋아하는지 ‘명시적으로’ 나타냈기 때문에 explicit feedback이라 부르며, 후자의 경우는 사용자가 해당 상품을 좋아했는지 싫어했는지 표현을 직접적으로 하지 않으므로 ‘implicit feedback’이라고 부른다. 이에 대해서는 나중에 다른 글을 통해 더 자세히 다루도록 하겠다. (Implicit feedback에 대한 글을 새로 추가하였다) 지금은 \(r_{ui}\)의 정확한 값을 알고 있고, 이 값이 전혀 noise가 없는 값이라고 가정하고 문제를 계속 설명하도록 하겠다. 이런 경우 우리가 가지고 있는 데이터는 \(r_{ui}\)들의 값이 될 것이고, 대략 아래와 같은 방식으로 matrix로 표현할 수 있을 것이다.

@@ -229,7 +229,7 @@
Methodology 2: Solve Non-convex Problem Directly

이 pair-wise optimization 문제는 non-convex 문제이지만, gradient descent method로 local optimum에 수렴하는 결과를 얻을 수 있으며 실제로 꽤 효율적으로 좋은 결과를 얻을 수 있다.

-

또 다른 solver로는 Alternating Least Square (ALS) 라는 방법이 있다. 이 방법은 alternative하게 주어진 objective를 update하는 방법인데, 주어진 objective가 pairwise optimization으로 생각하면 non-convex이지만, p나 q 중 하나를 고정하고 나머지에 대해 optimization을 하게 되면 convex, 그것도 closed form으로 계산된다는 점을 이용한 방법이다. 따라서 이 방법을 사용해 예전에 설명했었던 k-means style의 알고리즘을 설계할 수 있는데, 이를 ALS라고 부르는 것이다. Gradient descent가 더 빠른 경우도 있지만, ALS를 사용하게 되면 각각의 element들이 다른 element에 independent하기 떄문에 분산처리가 간편하기 때문에 실제로는 ALS 방법도 많이 사용된다고 한다.

+

또 다른 solver로는 Alternating Least Square (ALS) 라는 방법이 있다. 이 방법은 alternative하게 주어진 objective를 update하는 방법인데, 주어진 objective가 pairwise optimization으로 생각하면 non-convex이지만, p나 q 중 하나를 고정하고 나머지에 대해 optimization을 하게 되면 convex, 그것도 closed form으로 계산된다는 점을 이용한 방법이다. 따라서 이 방법을 사용해 예전에 설명했었던 k-means style의 알고리즘을 설계할 수 있는데, 이를 ALS라고 부르는 것이다. Gradient descent가 더 빠른 경우도 있지만, ALS를 사용하게 되면 각각의 element들이 다른 element에 independent하기 때문에 분산처리가 간편하기 때문에 실제로는 ALS 방법도 많이 사용된다고 한다.

지금까지 설명한 방법은 그야말로 가장 기본이 되는 모델이고, 이 모델을 조금 더 확장해보도록 하자. 가장 간단하게 확장할 수 있는 방법은 bias term을 추가하는 것이다. 예를 들어서 어떤 user는 항상 모든 영화 평점을 비교적 ‘짜게’ 주는 user가 있을 수 있고, 반대로 모든 영화에 점수를 후하게 주는 user도 있을 수 있다. 어떤 영화는 개봉 전부터 평단이나 기자들에게서 호평을 받았거나 유명 배우가 나와 기본 점수가 높을 수도 있고, 그 반대도 가능하다. 따라서 이런 현상들을 반영할 수 있는 bias term이 추가가 되는 것은 지극히 자연스럽다고 할 수 있다. user의 bias를 \(b_u\), item의 bias를 \(b_i\)라고 하면 (이 값들은 vector가 아니라 scalar value이다) user u의 item i에 대한 bias \(b_{ui}\)는 \(b_{ui} = \mu + b_u + b_i\)로 표현할 수 있을 것이다. 여기에서 \(\mu\)는 전체 모든 r_{ui}의 평균 값으로, bias라는 개념이 평균에서부터 얼마나 멀어지는 가에 대한 개념이므로 평균 값도 함께 고려하는 것이다. \(\mu\)는 데이터와 함께 주어지는 값이고, bias term들은 p,q처럼 optimization을 통해 찾아야하는 optimization parameter가 된다. 이를 사용하면 reconstructed rating \(\hat r_{ui}\)는 다음과 같이 표현된다.

@@ -271,7 +271,7 @@
Matrix Factorization in Netflix Prize Competition
정리
-

이 글에서는 recommendation 문제가 어떤 문제인지 설명하고, 보다 수학적으로 정의된 matrix completion 문제로 recommendation을 설명한다. 그 후 이 문제를 푸는 가장 popular한 방법인 matrix factorization에 대해서 다룬다. 해당 문제가 non-convex 문제이기 떄문에 convex relaxation을 통해 문제를 푸는 방법 (더 늦게 나온 방법이다), non-convex optimization을 바로 푸는 방법 (Netflix prize에서 실제 사용했던 방법) 두 가지를 소개하고 각각에 대해 간략하게 설명한다. 실제 recommendation은 matrix factorization 뿐 아니라 여러 다른 methodology들을 결합해서 문제를 풀게 되지만, 여전히 단일 model로 가장 좋은 performance를 보여주는 것은 matrix factorization을 기반으로 한 방법론들이기 떄문에 matrix factorization을 제대로 아는 것이 recommendation 문제를 풀기 위한 첫 걸음이라 할 수 있을 것이다.

+

이 글에서는 recommendation 문제가 어떤 문제인지 설명하고, 보다 수학적으로 정의된 matrix completion 문제로 recommendation을 설명한다. 그 후 이 문제를 푸는 가장 popular한 방법인 matrix factorization에 대해서 다룬다. 해당 문제가 non-convex 문제이기 때문에 convex relaxation을 통해 문제를 푸는 방법 (더 늦게 나온 방법이다), non-convex optimization을 바로 푸는 방법 (Netflix prize에서 실제 사용했던 방법) 두 가지를 소개하고 각각에 대해 간략하게 설명한다. 실제 recommendation은 matrix factorization 뿐 아니라 여러 다른 methodology들을 결합해서 문제를 풀게 되지만, 여전히 단일 model로 가장 좋은 performance를 보여주는 것은 matrix factorization을 기반으로 한 방법론들이기 때문에 matrix factorization을 제대로 아는 것이 recommendation 문제를 풀기 위한 첫 걸음이라 할 수 있을 것이다.

@@ -287,13 +287,13 @@
변경 이력
References
- +
    +
  1. 인터넷 속의 수학 - How Does Netflix Recommend Movies? (1/2)
  2. +
  3. 인터넷 속의 수학 - How Does Netflix Recommend Movies? (2/2)
  4. +
  5. Kennedy, Ryan. “Low-rank matrix completion.”, 2013
  6. +
  7. Candès, Emmanuel J., and Benjamin Recht. “Exact matrix completion via convex optimization.”, 2009
  8. +
  9. Koren, Yehuda, Robert Bell, and Chris Volinsky. “Matrix factorization techniques for recommender systems.”, 2009
  10. +
@@ -322,10 +322,20 @@
References
  • EM algorithm
  • Hidden Markov Model
  • Dimensionality Reduction (LDA, PCA)
  • -
  • Recommendation System (Matrix Completion)
  • +
  • Recommendation System (Matrix Completion) + + +
  • Neural Network Introduction
  • Deep Learning 1 – RBM, DNN, CNN
  • -
  • Reinforcement Learning
  • +
  • Reinforcement Learning + + +
  • @@ -403,6 +413,10 @@
    References

    +

    + » Machine learning 스터디 (17-1) Recommendation System with Implicit Feedback +

    + -->

    @@ -435,6 +449,10 @@

    Comments

    Recent Posts

    diff --git a/74/index.html b/74/index.html index 77b85bf5..c38b2656 100644 --- a/74/index.html +++ b/74/index.html @@ -537,6 +537,10 @@

    Comments

    Recent Posts

    diff --git a/75/index.html b/75/index.html index a3ad6972..682ba897 100644 --- a/75/index.html +++ b/75/index.html @@ -621,6 +621,10 @@

    Comments

    Recent Posts

    diff --git a/76/index.html b/76/index.html index cb6ffe2f..ee2ac59d 100644 --- a/76/index.html +++ b/76/index.html @@ -478,6 +478,10 @@

    Comments

    Recent Posts

    diff --git a/77/index.html b/77/index.html index 3f0cd02f..def64aa3 100644 --- a/77/index.html +++ b/77/index.html @@ -308,6 +308,10 @@

    Comments

    Recent Posts

    diff --git a/79/index.html b/79/index.html index 1a82ce27..3d2f23f1 100644 --- a/79/index.html +++ b/79/index.html @@ -246,6 +246,10 @@

    Comments

    Recent Posts

    diff --git a/8/index.html b/8/index.html index 6b02c028..7cf32bb2 100644 --- a/8/index.html +++ b/8/index.html @@ -229,6 +229,10 @@

    Comments

    Recent Posts

    diff --git a/80/index.html b/80/index.html index 2f37ecbb..b3b83f6c 100644 --- a/80/index.html +++ b/80/index.html @@ -282,6 +282,10 @@

    Comments

    Recent Posts

    diff --git a/81/index.html b/81/index.html index 3304faf3..fe4b3503 100644 --- a/81/index.html +++ b/81/index.html @@ -241,6 +241,10 @@

    Comments

    Recent Posts

    diff --git a/82/index.html b/82/index.html index 98adbc98..bc80a036 100644 --- a/82/index.html +++ b/82/index.html @@ -226,6 +226,10 @@

    Comments

    Recent Posts

    diff --git a/85/index.html b/85/index.html index 130888c0..e3cece64 100644 --- a/85/index.html +++ b/85/index.html @@ -235,6 +235,10 @@

    Comments

    Recent Posts

    diff --git a/86/index.html b/86/index.html index 8a2c7357..acba5327 100644 --- a/86/index.html +++ b/86/index.html @@ -266,6 +266,10 @@

    Comments

    Recent Posts

    diff --git a/87/index.html b/87/index.html index a1254fe5..1cbed6fe 100644 --- a/87/index.html +++ b/87/index.html @@ -274,6 +274,10 @@

    Comments

    Recent Posts

    diff --git a/88/index.html b/88/index.html index f7084081..38311591 100644 --- a/88/index.html +++ b/88/index.html @@ -428,6 +428,10 @@

    Comments

    Recent Posts

    diff --git a/89/index.html b/89/index.html index 508dbeee..e28de56b 100644 --- a/89/index.html +++ b/89/index.html @@ -322,6 +322,10 @@

    Comments

    Recent Posts

    diff --git a/9/index.html b/9/index.html index 4b2da277..a7aa2cd8 100644 --- a/9/index.html +++ b/9/index.html @@ -231,6 +231,10 @@

    Comments

    Recent Posts

    diff --git a/90/index.html b/90/index.html index a439beec..0306244b 100644 --- a/90/index.html +++ b/90/index.html @@ -422,6 +422,10 @@

    Comments

    Recent Posts

    diff --git a/91/index.html b/91/index.html index 7a31310a..a8a3382c 100644 --- a/91/index.html +++ b/91/index.html @@ -353,6 +353,10 @@

    Comments

    Recent Posts

    diff --git a/92/index.html b/92/index.html index cdd3e5d8..fac1fc17 100644 --- a/92/index.html +++ b/92/index.html @@ -454,6 +454,10 @@

    Comments

    Recent Posts

    diff --git a/93/index.html b/93/index.html index be3000a5..f84b7c16 100644 --- a/93/index.html +++ b/93/index.html @@ -558,6 +558,10 @@

    Comments

    Recent Posts

    diff --git a/95/index.html b/95/index.html new file mode 100644 index 00000000..33d7d4df --- /dev/null +++ b/95/index.html @@ -0,0 +1,603 @@ + + + + + + + + Machine learning 스터디 (17-1) Recommendation System with Implicit Feedback - README + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +

    README   + + SanghyukChun's Blog + +

    +
    +
    + +
    + +
    +
    +
    + +
    + +

    Machine Learning 스터디 (17-1) Recommendation System With Implicit Feedback

    + + +

    + + + + + + + + + + + + + + + | Comments + +

    + +
    + + +
    들어가며
    + + +

    이전 글[1]에서 다룬 recommendation system은 사용자가 점수를 정확하게 매긴 경우에 대해, 즉 explicit feedback에 대해서만 문제를 푸는 방식이다. 그러나 실제로는 사용자가 점수를 직접 매기는 대신에 단순히 클릭했거나, 조회, 구매한 간접적인 정보, 즉 implicit feedback에만 의존하게 되는 경우도 빈번하게 발생하게 된다. 이 글에서는 그런 implicit feedback만 존재하는 상황에서 어떻게 matrix completion 문제를 디자인하고 해결하는지에 대해 총 세 개의 논문을 들어 설명할 것이다.

    + + +

    이 글의 맨 앞은 implicit feedback이 정확히 무엇이고, 어떤 상황의 문제들이 있는지에 대해 설명할 것이다. 그리고 총 세 개의 논문에서 어떤 방법으로 문제를 접근하는가 설명하도록 할 것이다. 소개할 논문은 Collaborative Filtering for Implicit Feedback Datasets [2], Logistic Matrix Factorization for Implicit Feedback Data [3], Bayesian Personalized Ranking for Non-Uniformly Sampled Items [4] 총 세가지이다.

    + + + + +
    Explicit Feedback and Implicit Feedback
    + + +

    본격적으로 논문들이 제안한 방법론을 살펴보기 전에, explicit feedback과 implicit feedback의 차이점에 대해 논해보도록 하겠다. 먼저 explicit feedback은 사용자가 정확하게 본인이 얼마나 이 item에 호감이 있는지를 수치로 feedback을 주는 것이다. 예를 들어서 Netflix problem의 별점 데이터는 정확하게 1점부터 5점 사이의 well-define된 범위를 가진다. 그러나 실제로는 사용자의 item에 대한 정확한 호감도를 요구하는 것이 어려울 때가 있다. 아마존의 상품 추천을 예로 들어보자. 아마존이 가질 수 있는 데이터는 사용자가 어떤 상품들을 조회하였는지와, 어떤 상품들을 구매하였는지 정도의 정보 밖에 가질 수 없다. 이 경우 사용자가 살펴본 물건들이 사용자가 정말 매력있게 느껴서 살펴본 것인지, 만약 그렇다면 얼마만큼의 호감도가 있는지 판별하는 것이 매우 어려울 것이다. 이런 종류의 데이터를 사용자가 직접적인 점수를 주는 대신 간접적인 정보만을 제공한다고 하여 implicit feedback이라 부른다. 이외에도 sound cloud나 youTube 등에서 사용자가 재생한 재생목록이나 반복하여 청취 혹은 시청한 횟수 등의 데이터도 마찬가지로 implicit feedback의 대표적인 예가 될 수 있다.

    + + +

    Implicit feedback에서 관측 값은 click이나 재생 횟수 (0 혹은 0보다 큰 정수) 일 수도 있고, 음악 등의 item을 재생한 총 시간 (0 이상의 실수) 일 수도 있다. 한 가지 주의할 점은, explicit feedback처럼 사용자가 구체적으로 item에 대한 preference를 제공하지 않기 떄문에 사용자가 선호하지 않아서 선택하지 않은 item과 아직 관측하지 않은, 그러나 잠재적으로 흥미가 있는 item 모두 값이 0일 것이라는 점이다. 보통 사용자들이 item을 굉장히 조금만 click하거나 (뉴스) 사용하거나 (음악, 동영상) 구매하기 때문에 (쇼핑) 실제로는 matrix의 거의 대부분이 비어있고 아주 일부분의 데이터만 관측되기 때문에, negative observation이 positive observation의 수를 압도한게 되고, 때문에 이런 점을 고려하지 않고 모델을 설계하게 되면 아주 일부분의 positive 데이터와 거의 대부분의 negative observation (값이 0인 observation)들에의해 model이 overfitting된다. 또한, implicit feedback은 굉장히 노이즈가 많기 때문에 주어진 데이터를 얼마나 믿을 수 있을지 알 수 없다는 것이다. 예를 들어서 사용자가 물건을 하나 구매하였더라도, 이 물건에 대해 반드시 긍정적으로 생각할 것이라 기대할 수는 없을 것이다. 가끔은 구매한 물건이 아주 불만족스럽고 다시는 비슷한 물건을 구매하고 싶지 않을 수도 있지 않은가? 이런 두 가지 이슈 (negative observation, confidence)는 recommendation model이 implicit feedback을 처리하기 위해 반드시 고려되어야 할 이슈가 된다.

    + + + + +
    Recall: Matrix Factorization with Explicit Feedback
    + + +

    이전 글 [1]에서 다뤘던 objective function은 다음과 같다.

    + + +

    \[\min_{X,Y} \sum_{u,i \in \kappa} (r_{ui} - x_u^\top y_i)^2 + \lambda ( \| x_u \|_2^2 + \| y_i \|_2^2 ) .\]

    + + +

    이전 글에서는 x,y 대신 p,q noataion을 사용했으나 이 글에서는 전부 x, y notation으로 통일하도록 하겠다. 이 objective에 대해서는 이전 글을 참고하면서 읽으면 좋을 것 같다. 원래는 bias term까지 포함해야하지만, 이 글에서는 편의상 bias term은 생략하도록 하겠다. 이 문제는 SGD (Stochastic Gradient Descent), ALS (Alternating Least Square) 등의 solver를 사용해 풀 수 있으며 이 글에서는 자세한 설명을 생략하도록 하겠다.

    + + + + +
    Collaborative Filtering for Implicit Feedback Datasets [2]
    + + +

    Implicit feedback을 처리하는 가장 기본적인 접근법을 소개해보자. 이 논문은 user u가 item i를 선호하는지 하지 않는지 여부를 가르키는 preference vector \(p_{ui}\)를 정의한다. \(p_{ui}\)의 값은 \(sign(r_{ui})\)으로 정의할 수 있다. sign 함수는 input 값의 ‘sign’을 return하는 함수로, 즉 input이 negative value면 -1, positive value면 1을 return한다. 따라서 perference의 값은 rating r이 0보다 크다면 1이고, r이 0이라면 p도 0이 되는 것이다.

    + + +

    앞서 설명한 것 처럼, preference vector의 값을 항상 신뢰할 수 있는 것은 아니다. 때문에, 이 논문에서는 confience level \(c_{ui}\)라는 것을 정의하게 된다. 우리가 한 가지 가정할 수 있는 것은 만약 \(r_{ui}\)의 값이 크다면, 예를 들어 한 사용자가 한 항목을 엄청 많이 재구매했다거나 한다면, u는 i를 아주 높은 확률로 prefer한다는 사실을 가정할 수 있다. 따라서 confidence level은 r에 대한 increasing function으로 정의하는 것이 타당하다고 할 수 있다. 이 논문에서는 confidence level \(c_{ui}\)를 다양한 방식으로 정의할 수 있다고 언급하고 있으며, 실제 실험에서는 가장 직관적이고 단순한 increasing function은 linear function을 사용한다. 따라서 이 논문에서는 다음과 같은 confidence를 사용한다.

    + + +

    \[c_{ui} = 1 + \alpha r_{ui}.\]

    + + +

    혹은 \(c_{ui} = 1 + \alpha \log (1 + r_{ui}/ \varepsilon)\) 등의 confidence도 대안으로 제안하기는 하지만, 기본적으로 위에서 설명한 linear confidence를 사용하는 듯하다. 한 가지 짚고 넘어가야할 점은, \(c_{ui}\)는 실제 데이터 \(r_{ui}\)와 hyper-parameter \(\alpha\)에 의해서만 정의되므로 optimize해야 할 parameter가 아니라 한 번 confidence를 정의하기만하면 고정되는 constant라는 점이다. 따라서 confidence의 값을 어떻게 정의하더라도 전체 알고리즘의 로직을 바꾸거나 하지는 않는다.

    + + +

    c를 정의하는 것에는 또 하나의 이점이 있다. Parameter \(\alpha\)가 positive observation과 negative observation의 중요도를 조절하는 역할을 하게 되면서, negative feedback에 대한 중요도를 조절할 수 있는 것이다. 예를 들어 \(\alpha\)의 크기가 작다면, positive와 negative observation의 confidence 차이가 큰 \(\alpha\)를 가질 때 보다 상대적으로 작을 것이라는 것을 기대할 수 있게 된다.

    + + +

    이제 objective를 정의할 차례이다. Explicit feedback에서는 복원한 rating \(\hat r_{ui}\)와 관측한 데이터 \(r_{ui}\)의 RMSE를 바로 계산하였으나, 앞서 말한대로 이 값을 그대로 계산하기에는 confidence의 문제가 있다. 이 논문에서는 앞서 정의한 confidence를 고려하여 objective function은 다음과 같이 정의한다.

    + + +

    \[\min_{X,Y} \sum_{u,i \in \kappa} c_{ui}(p_{ui} - x_u^\top y_i)^2 + \lambda ( \| x_u \|_2^2 + \| y_i \|_2^2 ) .\]

    + + +

    맨 처음 objective와 달라진 점은, rating vector r (0 이상의 real value) 이 preference vector p (0 또는 1) 로 바뀌었다는 점과, 각각의 u,i pair에 대해 confidence \(c_{ui}\)가 곱해진다는 점이다. 이때, \(c_{ui}\)는 optimization parameter와는 상관없이 맨 처음 정해지고 변하지 않는 값이므로, 이렇게 바뀐 objective function을 풀기 위해서 이전 문제와 마찬가지로 살짝 변형된 SGD나 ALS 등을 사용할 수 있다. 논문에서는 조금 더 scalability를 고려한 방법론을 제안하는데, matrix product를 조금 더 효율적으로 하도록 matrix들을 decompose하여 조금 더 order가 낮은 연산을 하는 방법을 사용한다. 자세한 알고리즘은 논문을 참고하면 좋을 것 같다.

    + + +

    정리하자면 이 논문은 rating vector r을 preference vector p로 변환하고, confidence level c를 정의한 후, p와 c를 사용해 RMSE objective function을 optimize하는 work인 것이다. 그리고 앞서 설명했던 두 가지 문제는 confidence level c를 정의하는 방법에 의해 해결할 수 있다.

    + + + + +
    Logistic Matrix Factorization for Implicit Feedback Data [3]
    + + +

    앞선 논문 [2]에서는 RMSE를 minimize하는 objective를 설계하였지만, RMSE가 아닌 다른 형태의 objective를 optimize하는 것도 가능하다. 앞선 논문에서 RMSE를 minimize함으로써 얻을 수 있는 효과는 관측한 preference \(p_{ui}\)와 복원한 preference \(\hat p_{ui}\)가 서로 (RMSE의 관점에서) 최대한 비슷한 값을 가지도록 optimization이 된다는 것이다. 이 논문은 perference의 RMSE를 minimize하는 문제 대신, 관측 값 \(r\)과 optimization parameter \(\Theta = (x, y, b)\) 등의 posteriori를 maximization하는 방식을 취한다. 참고로 이 논문은 Spotify에서 발표한 논문으로, 실제 음악 추천에서 응용하고 있는 듯 하다.

    + + +

    계속 강조하듯, 여기에서 실제 유저가 선호하는 것과 유저의 implicit feedback 결과는 다를 수 있다. 그렇기 때문에 이 논문은 u가 i를 좋아할 확률을 logistic function으로 확률적으로 정의한 후, 관측한 데이터로부터 posteriori를 maximize하는 방향으로 학습을 하게 된다. 그러기위해 이 논문은 \(\ell_{ui}\)이라는 새로운 notation을 introduce하고 이를 사용자 u가 item i를 선호하는 ‘event’로 정의한다. 어렵게 설명했지만, 그냥 ‘user u가 음악 i를 좋아한다 좋아하지 않는다’에 대한 0, 1 값이다. 이 논문은 주어진 \(\Theta (x, y, b)\)에 대해 \(\ell\)이 1이 될 확률 \(pr_{ui}\)를 다음과 같이 logistic form으로 정의한다.

    + + +

    \[pr_{ui} := Pr[\ell_{ui} = 1 ~|~ \Theta] = \frac{\exp(x_u^\top y_i + b_u + b_i)}{1 + \exp(x_u^\top y_i + b_u + b_i)}. \]

    + + +

    직관적으로 생각해보았을 때, 앞선 논문 [2]은 r의 값이 0보다 크기만 하면 항상 u가 i를 좋아한다고 생각하지만, 이 논문에서는 그것이 r의 값에 대한 확률로 나타난다는 점을 알 수 있다. Reconstructed rating \(\hat r\)을 \(\hat r_{ui} = x_u^\top y_i\)이라고 했을 때, 위의 함수는 \(\hat r_{ui}\)에 대한 증가함수이므로 (\(\hat r_{ui}\)의 값이 \(-\infty\)가 되면 함수값이 0이고, \(\hat r_{ui}\) 값이 \(\infty\)가 되면 함수값이 1이 된다), 위의 식은 rating 값이 더 크면 호감을 가질 확률이 더 높아지는 형태의 확률 함수가 된다.

    + + +

    따라서 이 모델의 likelihood는 positive observation u,i의 set을 \(\mathcal S\)라 하였을 때 다음과 같이 쓸 수 있다.

    + + +

    \[\mathcal L_{naive} (R ~|~ \Theta) = \prod_{u,i \in \mathcal S} pr_{ui} \prod_{u,i \notin \mathcal S} (1-pr_{ui})\]

    + + +

    그러나 앞선 논문 [2]에서도 언급되었듯, negative feedback은 ‘싫어한다’와 다른 의미를 가지고 있기 때문에, 이 논문에서도 confidence라는 것을 정의하게 된다. [2]와의 차이점이라면 RMSE 관점이 아니라 앞에서 살펴본 likelihood function의 관점에서 정의를 한다는 점이다. 여기에서 \(c_{ui}\)는 \(\alpha r_{ui}\)로 사용하는데, 만약 hyper-parameter \(\alpha\)의 값이 크면 positive observation에 더 큰 비중을 두고, \(\alpha\)의 값이 작다면 negative observation에 더 큰 비중을 두는 식으로 다음과 같이 정의를 하게 된다.

    + + +

    \[ \mathcal L (R ~|~ \Theta) = \prod_{u,i} Pr[ \ell_{ui} | \Theta]^{\alpha r_{ui}} (1 - Pr[ \ell_{ui} | \Theta]).\]

    + + +

    위의 식에서 negative feedback에 대한 (즉, 만약 관측값 \(r_{ui}\)가 0이라면) likelihood 값은 \(\mathcal L (r_{ui} ~|~ \Theta ) = 1 - pr_{ui}\) 가 되므로 앞에서 계산한 naive한 likelihood function과 일치한다. 그러나 positive feedback에 대한 likelihood는 \(pr_{ui}^{\alpha r_{ui}} (1-pr_{ui}) \)가 되므로, \(\alpha\)의 값을 조절함에 따라 앞에서 계산한 값과 차이가 있다.

    + + +

    개인적인 생각으로는 여기에서 저자가 증명을 잘못한 것이 아닐까 생각된다. 만약 Positive feedback의 likelihood에 weight를 주기위해 exponent c를 추가한다고 했을 때, likelihood 식은 다음과 같이 된다.

    + + +

    \[ \mathcal L = \prod_{u,i \in \mathcal S} ( p_{ui}^{\ell_{ui}} (1-p_{ui})^{1-\ell_{ui}} )^{\alpha r_{ui}} \prod_{u,i \notin \mathcal S} p_{ui}^{\ell_{ui}} (1-p_{ui})^{1-\ell_{ui}}. \]

    + + +

    이때, \(r_{ui} > 0\) 일 때 \(\ell = 1\)이고, 혹은 둘 다 0이라는 특성을 잘 사용하면 이 식은 다음과 같이 표현이 된다.

    + + +

    \[\mathcal L = \prod_{u,i} p_{ui}^{\alpha r_{ui}} (1-p_{ui})^{1-\ell_{ui}}. \]

    + + +

    즉, 만약 저자가 의도한대로 positive feedback의 likelihood에 exponent로 weight를 주고 싶었다면, positive feedback의 likelihood function에서 1-p 부분이 없어야한다는 점이다. 이 부분은 저자가 실수를 했거나 혹은 내가 이해를 잘못했을 가능성이 있다. 혹시 몰라서 논문을 좀 찾아봤는데, 약 한 달 전쯤 나온 논문에서 증명한 결과가 내가 증명한 결과와 같은걸 보면, 저자가 틀린게 맞는 것 같다.

    + + +

    그리고 또 다른 문제는 \(pr_{ui}\)는 언제나 값이 0에서 1사이이기 때문에 \(c_{ui} = \alpha r_{ui}\)의 값이 크면 클수록 \(pr_{ui}\)의 값은 오히려 감소하게 된다는 것이다. 그래서 논문의 설명과는 반대로 \(\alpha\)의 값을 키우는 것이 오히려 positive observation의 weight를 낮추는 것이 아닌가하는 생각이 드는데, 논문을 여러 번 다시 읽어보고 계산을 해봐도 아직 아리송하다. 오히려 이렇게 하고 싶었다면, 최종 objective가 완전히 바뀌기는 하지만, 관측된 데이터 \(\mathcal S\)에 대해서 likelihood를 \( (1 + \alpha_{ui}) pr_{ui}\)와 같은 형태로 정의하는 편이 훨씬 좋지 않을까? 왜 base가 1보다 작은데, weight term을 exponent으로 올렸는지 이해가 되질 않는다. 이 부분은 혹시 나중에 이해가 완전히 되면 내용을 추가하도록 하겠다.

    + + +

    다시 본문으로 돌아오자. Likelihood를 계산했으니, prior만 있다면 posteriori를 계산할 수 있게 된다. 이 논문은 \(x, y\)의 prior를 전부 0 mean Normal distribution으로 정의한다. 이렇게 정의할 경우, 나중에 log MAP 문제를 풀게 될 때, L2 regularization과 같은 형태의 식 \(\frac{\lambda}{2} (\| x \|^2 + \| y \|^2) \) 을 얻을 수 있다. 자세한 증명은 생략한다. Prior를 정했으니 이제 posteriori를 구해서 다음과 같이 log MAP 문제를 정의할 수 있다. (논문에서 증명한 결과로, 내가 증명한 결과와는 차이가 있다)

    + + +

    \[ \log Pr[ \Theta | P ] = \sum_{u,i} c_{ui} \widehat r_{ui} - ( 1 + c_{ui} ) \log ( 1 + \exp \widehat r_{ui} ) - \frac{\lambda}{2} ( \| x_u\|^2 + \| y_i \|^2 ). \]

    + + +

    이 문제 역시 다른 문제들 처럼 한 번에 update하기가 어렵기 떄문에 alternative하게 update를 하게 된다. 정확히는 coordinate gradient method를 사용한다 (maximize문제이므로 ascent가 될 것이다). 여기에서 한 가지 문제가 발생하는데, 앞에 붙어있는 summation term이 모.든. (u,i) pair에 대한 summation이기 때문에 한 번 gradient를 계산하는 비용이 어마어마해진다는 것이다. 정확히는 아이템의 개수 I와 유저의 숫자 U에 대해 linear한 비용이 필요하다. 보통 그 둘의 값은 몇 백만, 몇 천만이 될 정도로 크기 때문에 scalability 이슈가 굉장히 중요해진다. 이 논문은 그런 문제를 해결하기 위하여 (속도가 느리다는 문제) AdaGradient를 사용하라거나, 전체에 대해 summation을 하는 대신, 전체 positive pair (u,i)와 일부 negative pair (u,i)만 사용해서 문제를 해결하라고 언급되어있다.

    + + +

    정리하자면 이 논문은 Matrix Factorization을 RMSE minimization 문제가 아닌 MAP 문제로 해결하려는 시도를 한 논문으로, MAP로 바꾸기 위하여 confidence가 포함된 likelihood function을 정의한다. (개인적으로는 이 likelihood function이 왜 이런 꼴을 하고 있는지 이해하지 못하였다) 알고리즘은 coordinate ascent를 사용하지만, 각각의 gradient 값이 아이템과 유저의 개수에 linear하기 때문에 실제 데이터에서 practical하지 못하다는 문제가 발생한다. 이런 문제를 해결하기 위하여 이 논문은 전체 matrix의 거의 대부분을 차지하는 negative observation을 전체 다 사용하는 대신, 일부만 sample하여 사용하는 방식을 제안하고 있다.

    + + + + +
    Bayesian Personalized Ranking for Non-Uniformly Sampled Items [4]
    + + +

    앞의 두 논문은 같은 문제의 objective function만 RMSE와 MAP로 서로 다르게 잡은 경우이지만, 이 논문은 앞의 방법들과 다소 다른 접근 방식을 취하고 있다. 이 논문은 먼저 선행 연구[5]를 조금 발전 시킨 논문인데, 선행 연구에서는 partially observed pair-wise competition 문제를 푸는 Baysian Personalized Ranking (BPR) optimization과 그것을 푸는 알고리즘을 제안하고, 그것을 MF로 확장하고있다. 그리고 그 다음 논문 [4]에서는 원래 논문이 가지는 단점을 negative observation을 adaptive하게 sample하는 방식으로 개선하고 있다.

    + + +

    먼저 핵심 notation들을 정의해보자. 관측된 (u,i) pair는 \(\mathcal S\)라는 set으로 정의된다. 여기에서 새로운 notation \(I_u^+\)와 \(U_i^+\) 2개가 introduce된다. \(I_u^+\)는 user u가 관측한 적 있는 item의 set이고, \(U_i^+\)는 item i를 관측한 적 있는 user u의 set이다. 그러면 이 set들을 통해 \(\mathcal D_S\)라는 triple을 다음과 같이 정의할 수 있다.

    + + +

    \[D_S := {(u,i,j) ~|~ i \in I_u^+ \mbox{ and } j \notin I_u^+ }.\]

    + + +

    즉, user u, user u가 관측한 item i와 관측하지 못한 item j 이렇게 셋의 triple인 것이다.

    + + +

    이제 이 논문의 핵심아이디어인 pair-wise ranking에 대해 살펴보자. 이 논문은 먼저 각각의 user u에게 item i와 item j간의 pair-wise ranking이 존재한다고 가정한다. 이 논문에서는 user u가 item i를 j보다 높은 order를 가질 때 \(i >_u j\)의 꼴로 표현한다. 이 pair-wise ranking은 (혹은 order는) totality, antisymmetry, transitivity를 만족하는 order로 정의된다. 자세한 수식은 논문에 있으니 생략한다.

    + + +

    이 논문은 user u가 item i를 item j보다 더 높게 평가할 확률을 다음과 같이 가정하고 있다.

    + + +

    \[Pr( i >_u j | \Theta) = \sigma(\hat r_{uij} (\Theta)).\]

    + + +

    여기에서 \(\hat r_{uij} := \hat r_{ui} - \hat r_{uj}\)로 정의되는 값이고, \(\sigma\)는 sigmoid function으로, \(\sigma(x) = \frac{1}{1+\exp(-x)}\)의 꼴로 정의된다. 각각의 user에 대해 pair-wise ranking에 대한 확률을 정의했고, 또한 실제 관측값도 있기 때문에, 우리는 ranking에 대한 likelihood를 정의할 수 있고, prior를 가정하게 되면 posteriori역시 계산할 수 있다. 먼저 likelihood \(\prod_u p(>_u | \Theta)\)부터 계산해보자. (이전과 마찬가지로 user들은 전부 independent하다고 가정했기 때문에 곱으로 표현된다)

    + + +

    \[\prod_u p(>_u | \Theta) = \prod_{u,i,j} \prod_u p( i >_u j | \Theta)^{I_{(u,i,j) \in \mathcal D_S}} (1 - p( i >_u j | \Theta) )^{I_{(u,i,j) \notin \mathcal D_S}} \]

    + + +

    \(I_x\)는 x가 true면 1이고 false면 0인 indicator function이다. 여기에서 order를 정의할 때 totality와 antisymmetry를 가정하였기 때문에, 위 식을 잘 건개하면 아래와 같은 간단한 식으로 표현하는 것이 가능하다고 한다.

    + + +

    \[\prod_u p(>_u | \Theta) = \prod_{(u,i,j) \in \mathcal D_S} p( i >_u j | \Theta). \]

    + + +

    [3]과 같이 prior를 zero mean normal distribution으로 정의하게 되면, log posteriori는 다음과 같이 표현된다.

    + + +

    \[ \max \ln Pr(\Theta | >_u) = \max \sum_{(u,i,j) \in \mathcal D_S} \ln \sigma(\hat x_{uij}) - \lambda \| \Theta \|^2. \]

    + + +

    이 문제는 stochastic gradient descent로 푸는 것이 가능하다. 자세한 미분 값은 논문에 있으니 생략한다. 참고로 이 논문은 이 문제를 optimization했을 때 얻을 수 있는 solution이 AUC (area under the ROC curve, ROC curve는 wiki 참고)를 maximization하는 것과 비슷한 문제라는 것을 따로 증명해두었으니 관심이 있다면 한 번쯤 읽어보면 좋을 것 같다.

    + + +

    [4]에서 제안한 BPR (baysian personalized ranking) problem을 풀기 위해서 SGD를 사용한다는 언급을 했는데, 문제가 하나 있다. 바로 triple \(\mathcal D_S\)의 개수가 너무 많아서 전부 uniform하게 뽑기에는 데이터가 너무너무 많다는 것이다. 그래서 [5]에서는 adapted sampling 방식을 제안하고 있다.

    + + +

    \[ \max \sum_{(u,i,j) \in \mathcal D_S} w_u w_i w_j \ln \sigma(\hat x_{uij}) - \lambda \| \Theta \|^2. \]

    + + +

    이때 \(w_u = 1/| I_u^+ |\), \(w_i = 1\)로 정의가 된다. 즉, 관측한 아이템이 더 많은 유저는 적게 뽑고, 모든 positivie item은 uniform하게 뽑는다. 마지막으로 \(w_j = \frac{1}{|U||I|} \sum_{u} I_{j \in I_u^+}\)으로 취하게 되는데, 더 많이 사용자들이 관측한, 혹은 좋아한 데이터 위주로 sample을 뽑는 방식이다.

    + + +

    정리해보면, BPR은 다른 논문들처럼 reconstructed error를 바로 measure하는 것이 아니라, pair-wise ranking을 정의하고, 복원된 rating \(\hat r_{ui}\)에 대해 user가 item i보다 j를 좋아할 확률을 sigmoid로 정의한다. 이 확률을 사용해 MAP문제를 정의하는데, 이 문제는 ROC curve의 넓이를 구하는 것과 비슷한 문제가 된다. 이때, sigmoid function을 step function으로 바꾸면 완전히 ROC curve의 넓이를 구하는 것과 같은 문제가 된다. Sigmoid가 step function의 가장 popular한 differentiable approximation 중 하나이기 때문에 sigmoid로 정의하게 되는 것이다. Algorithm은 SGD를 사용하는데, 데이터 셋이 user u가 관측한 item i와 관측하지 않은 item j의 triple이기 때문에 uniform sampling을 하게 되면 결과가 좋지 않을 수 있다. 때문에 adaptive하게 (u,i,j)에서 j 고를 때, popular한 j를 더 고르도록 sample을 하여 성능을 개선하고 있다.

    + + + + +
    정리
    + + +

    이 글에서는 Implicit feedback에 대해 recommendation을 어떻게 할 수 있을지 서로 다른 세 가지 접근방법을 소개했다. 첫 번째는 가장 기본적인 방법으로, confidence level \(c_{ui}\)를 정의하고, real value variable인 \(r_{ui}\)를 binary variable인 \(p_{ui}\)로 바꾼 다음 optimization을 푸는 방법에 대해 소개했다. 두 번째로는 RMSE를 optimization하는 대신, u가 i를 좋아할 확률을 모델링하고, 주어진 데이터에 대해 MAP를 푸는 방법에 대해 소개했다. 마지막으로는 각각의 user별로 item들끼리의 personalized pair-wise ranking을 정의하고, 역시 마찬가지로 u가 i보다 j를 좋아할 확률을 모델링하고 이것의 MAP를 구하는 방법에 대해 소개했다. 알고리즘은 SGD로 해결할 수 있지만, 조금 더 smart하게 item을 뽑는 adaptive sampling을 사용할 경우 성능이 더 올라간다고 한다.

    + + + + +
    변경 이력
    + + +
      +
    • 2016년 3월 6일: 글 등록
    • +
    + + +
    References
    + + +
      +
    1. Recommendation System (Matrix Completion)
    2. +
    3. Hu, Yifan, Yehuda Koren, and Chris Volinsky. “Collaborative filtering for implicit feedback datasets.”, 2008
    4. +
    5. Johnson, Christopher C. “Logistic matrix factorization for implicit feedback data.”, 2014
    6. +
    7. Gantner, Zeno, et al. “Bayesian personalized ranking for non-uniformly sampled items.”, 2012
    8. +
    9. Rendle, Steffen, et al. “BPR: Bayesian personalized ranking from implicit feedback.”, 2009
    10. +
    + + + + +
    + + +

    Machine Learning 스터디의 다른 글들

    + + + + +
    + +
    +
    +

    + + + + + + + + + + + + + + + + + + +

    +

    + + + + + Machine-Learning, Machine-Learning-Study + + + + +

    + + + + + +

    +
    +
    + +
    +

    Comments

    +
    +
    +
    + + +
    + + + + +
    +
    + + + + +
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + + diff --git a/archives/index.html b/archives/index.html index 3ecb825a..22183edc 100644 --- a/archives/index.html +++ b/archives/index.html @@ -9,9 +9,9 @@ - + @@ -92,6 +92,21 @@

    Archives

    2016

    +
    + +


    +Machine learning 스터디 (17-1) Recommendation System with Implicit Feedback
    +내 멋대로 정리해보는 Machine Learning. Recommendation System with Implicit Feedback

    + +Machine-Learning, Machine-Learning-Study + + + + +
    + + +


    diff --git a/atom.xml b/atom.xml index 03d7b14f..55b32961 100644 --- a/atom.xml +++ b/atom.xml @@ -4,7 +4,7 @@ <![CDATA[README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ @@ -13,6 +13,273 @@ Octopress + + <![CDATA[Machine learning 스터디 (17-1) Recommendation System with Implicit Feedback]]> + + 2016-03-06T01:30:00+09:00 + http://SanghyukChun.github.io/95 + 들어가며 + + +

    이전 글[1]에서 다룬 recommendation system은 사용자가 점수를 정확하게 매긴 경우에 대해, 즉 explicit feedback에 대해서만 문제를 푸는 방식이다. 그러나 실제로는 사용자가 점수를 직접 매기는 대신에 단순히 클릭했거나, 조회, 구매한 간접적인 정보, 즉 implicit feedback에만 의존하게 되는 경우도 빈번하게 발생하게 된다. 이 글에서는 그런 implicit feedback만 존재하는 상황에서 어떻게 matrix completion 문제를 디자인하고 해결하는지에 대해 총 세 개의 논문을 들어 설명할 것이다.

    + + +

    이 글의 맨 앞은 implicit feedback이 정확히 무엇이고, 어떤 상황의 문제들이 있는지에 대해 설명할 것이다. 그리고 총 세 개의 논문에서 어떤 방법으로 문제를 접근하는가 설명하도록 할 것이다. 소개할 논문은 Collaborative Filtering for Implicit Feedback Datasets [2], Logistic Matrix Factorization for Implicit Feedback Data [3], Bayesian Personalized Ranking for Non-Uniformly Sampled Items [4] 총 세가지이다.

    + + + + +
    Explicit Feedback and Implicit Feedback
    + + +

    본격적으로 논문들이 제안한 방법론을 살펴보기 전에, explicit feedback과 implicit feedback의 차이점에 대해 논해보도록 하겠다. 먼저 explicit feedback은 사용자가 정확하게 본인이 얼마나 이 item에 호감이 있는지를 수치로 feedback을 주는 것이다. 예를 들어서 Netflix problem의 별점 데이터는 정확하게 1점부터 5점 사이의 well-define된 범위를 가진다. 그러나 실제로는 사용자의 item에 대한 정확한 호감도를 요구하는 것이 어려울 때가 있다. 아마존의 상품 추천을 예로 들어보자. 아마존이 가질 수 있는 데이터는 사용자가 어떤 상품들을 조회하였는지와, 어떤 상품들을 구매하였는지 정도의 정보 밖에 가질 수 없다. 이 경우 사용자가 살펴본 물건들이 사용자가 정말 매력있게 느껴서 살펴본 것인지, 만약 그렇다면 얼마만큼의 호감도가 있는지 판별하는 것이 매우 어려울 것이다. 이런 종류의 데이터를 사용자가 직접적인 점수를 주는 대신 간접적인 정보만을 제공한다고 하여 implicit feedback이라 부른다. 이외에도 sound cloud나 youTube 등에서 사용자가 재생한 재생목록이나 반복하여 청취 혹은 시청한 횟수 등의 데이터도 마찬가지로 implicit feedback의 대표적인 예가 될 수 있다.

    + + +

    Implicit feedback에서 관측 값은 click이나 재생 횟수 (0 혹은 0보다 큰 정수) 일 수도 있고, 음악 등의 item을 재생한 총 시간 (0 이상의 실수) 일 수도 있다. 한 가지 주의할 점은, explicit feedback처럼 사용자가 구체적으로 item에 대한 preference를 제공하지 않기 떄문에 사용자가 선호하지 않아서 선택하지 않은 item과 아직 관측하지 않은, 그러나 잠재적으로 흥미가 있는 item 모두 값이 0일 것이라는 점이다. 보통 사용자들이 item을 굉장히 조금만 click하거나 (뉴스) 사용하거나 (음악, 동영상) 구매하기 때문에 (쇼핑) 실제로는 matrix의 거의 대부분이 비어있고 아주 일부분의 데이터만 관측되기 때문에, negative observation이 positive observation의 수를 압도한게 되고, 때문에 이런 점을 고려하지 않고 모델을 설계하게 되면 아주 일부분의 positive 데이터와 거의 대부분의 negative observation (값이 0인 observation)들에의해 model이 overfitting된다. 또한, implicit feedback은 굉장히 노이즈가 많기 때문에 주어진 데이터를 얼마나 믿을 수 있을지 알 수 없다는 것이다. 예를 들어서 사용자가 물건을 하나 구매하였더라도, 이 물건에 대해 반드시 긍정적으로 생각할 것이라 기대할 수는 없을 것이다. 가끔은 구매한 물건이 아주 불만족스럽고 다시는 비슷한 물건을 구매하고 싶지 않을 수도 있지 않은가? 이런 두 가지 이슈 (negative observation, confidence)는 recommendation model이 implicit feedback을 처리하기 위해 반드시 고려되어야 할 이슈가 된다.

    + + + + +
    Recall: Matrix Factorization with Explicit Feedback
    + + +

    이전 글 [1]에서 다뤘던 objective function은 다음과 같다.

    + + +

    \[\min_{X,Y} \sum_{u,i \in \kappa} (r_{ui} - x_u^\top y_i)^2 + \lambda ( \| x_u \|_2^2 + \| y_i \|_2^2 ) .\]

    + + +

    이전 글에서는 x,y 대신 p,q noataion을 사용했으나 이 글에서는 전부 x, y notation으로 통일하도록 하겠다. 이 objective에 대해서는 이전 글을 참고하면서 읽으면 좋을 것 같다. 원래는 bias term까지 포함해야하지만, 이 글에서는 편의상 bias term은 생략하도록 하겠다. 이 문제는 SGD (Stochastic Gradient Descent), ALS (Alternating Least Square) 등의 solver를 사용해 풀 수 있으며 이 글에서는 자세한 설명을 생략하도록 하겠다.

    + + + + +
    Collaborative Filtering for Implicit Feedback Datasets [2]
    + + +

    Implicit feedback을 처리하는 가장 기본적인 접근법을 소개해보자. 이 논문은 user u가 item i를 선호하는지 하지 않는지 여부를 가르키는 preference vector \(p_{ui}\)를 정의한다. \(p_{ui}\)의 값은 \(sign(r_{ui})\)으로 정의할 수 있다. sign 함수는 input 값의 ‘sign’을 return하는 함수로, 즉 input이 negative value면 -1, positive value면 1을 return한다. 따라서 perference의 값은 rating r이 0보다 크다면 1이고, r이 0이라면 p도 0이 되는 것이다.

    + + +

    앞서 설명한 것 처럼, preference vector의 값을 항상 신뢰할 수 있는 것은 아니다. 때문에, 이 논문에서는 confience level \(c_{ui}\)라는 것을 정의하게 된다. 우리가 한 가지 가정할 수 있는 것은 만약 \(r_{ui}\)의 값이 크다면, 예를 들어 한 사용자가 한 항목을 엄청 많이 재구매했다거나 한다면, u는 i를 아주 높은 확률로 prefer한다는 사실을 가정할 수 있다. 따라서 confidence level은 r에 대한 increasing function으로 정의하는 것이 타당하다고 할 수 있다. 이 논문에서는 confidence level \(c_{ui}\)를 다양한 방식으로 정의할 수 있다고 언급하고 있으며, 실제 실험에서는 가장 직관적이고 단순한 increasing function은 linear function을 사용한다. 따라서 이 논문에서는 다음과 같은 confidence를 사용한다.

    + + +

    \[c_{ui} = 1 + \alpha r_{ui}.\]

    + + +

    혹은 \(c_{ui} = 1 + \alpha \log (1 + r_{ui}/ \varepsilon)\) 등의 confidence도 대안으로 제안하기는 하지만, 기본적으로 위에서 설명한 linear confidence를 사용하는 듯하다. 한 가지 짚고 넘어가야할 점은, \(c_{ui}\)는 실제 데이터 \(r_{ui}\)와 hyper-parameter \(\alpha\)에 의해서만 정의되므로 optimize해야 할 parameter가 아니라 한 번 confidence를 정의하기만하면 고정되는 constant라는 점이다. 따라서 confidence의 값을 어떻게 정의하더라도 전체 알고리즘의 로직을 바꾸거나 하지는 않는다.

    + + +

    c를 정의하는 것에는 또 하나의 이점이 있다. Parameter \(\alpha\)가 positive observation과 negative observation의 중요도를 조절하는 역할을 하게 되면서, negative feedback에 대한 중요도를 조절할 수 있는 것이다. 예를 들어 \(\alpha\)의 크기가 작다면, positive와 negative observation의 confidence 차이가 큰 \(\alpha\)를 가질 때 보다 상대적으로 작을 것이라는 것을 기대할 수 있게 된다.

    + + +

    이제 objective를 정의할 차례이다. Explicit feedback에서는 복원한 rating \(\hat r_{ui}\)와 관측한 데이터 \(r_{ui}\)의 RMSE를 바로 계산하였으나, 앞서 말한대로 이 값을 그대로 계산하기에는 confidence의 문제가 있다. 이 논문에서는 앞서 정의한 confidence를 고려하여 objective function은 다음과 같이 정의한다.

    + + +

    \[\min_{X,Y} \sum_{u,i \in \kappa} c_{ui}(p_{ui} - x_u^\top y_i)^2 + \lambda ( \| x_u \|_2^2 + \| y_i \|_2^2 ) .\]

    + + +

    맨 처음 objective와 달라진 점은, rating vector r (0 이상의 real value) 이 preference vector p (0 또는 1) 로 바뀌었다는 점과, 각각의 u,i pair에 대해 confidence \(c_{ui}\)가 곱해진다는 점이다. 이때, \(c_{ui}\)는 optimization parameter와는 상관없이 맨 처음 정해지고 변하지 않는 값이므로, 이렇게 바뀐 objective function을 풀기 위해서 이전 문제와 마찬가지로 살짝 변형된 SGD나 ALS 등을 사용할 수 있다. 논문에서는 조금 더 scalability를 고려한 방법론을 제안하는데, matrix product를 조금 더 효율적으로 하도록 matrix들을 decompose하여 조금 더 order가 낮은 연산을 하는 방법을 사용한다. 자세한 알고리즘은 논문을 참고하면 좋을 것 같다.

    + + +

    정리하자면 이 논문은 rating vector r을 preference vector p로 변환하고, confidence level c를 정의한 후, p와 c를 사용해 RMSE objective function을 optimize하는 work인 것이다. 그리고 앞서 설명했던 두 가지 문제는 confidence level c를 정의하는 방법에 의해 해결할 수 있다.

    + + + + +
    Logistic Matrix Factorization for Implicit Feedback Data [3]
    + + +

    앞선 논문 [2]에서는 RMSE를 minimize하는 objective를 설계하였지만, RMSE가 아닌 다른 형태의 objective를 optimize하는 것도 가능하다. 앞선 논문에서 RMSE를 minimize함으로써 얻을 수 있는 효과는 관측한 preference \(p_{ui}\)와 복원한 preference \(\hat p_{ui}\)가 서로 (RMSE의 관점에서) 최대한 비슷한 값을 가지도록 optimization이 된다는 것이다. 이 논문은 perference의 RMSE를 minimize하는 문제 대신, 관측 값 \(r\)과 optimization parameter \(\Theta = (x, y, b)\) 등의 posteriori를 maximization하는 방식을 취한다. 참고로 이 논문은 Spotify에서 발표한 논문으로, 실제 음악 추천에서 응용하고 있는 듯 하다.

    + + +

    계속 강조하듯, 여기에서 실제 유저가 선호하는 것과 유저의 implicit feedback 결과는 다를 수 있다. 그렇기 때문에 이 논문은 u가 i를 좋아할 확률을 logistic function으로 확률적으로 정의한 후, 관측한 데이터로부터 posteriori를 maximize하는 방향으로 학습을 하게 된다. 그러기위해 이 논문은 \(\ell_{ui}\)이라는 새로운 notation을 introduce하고 이를 사용자 u가 item i를 선호하는 ‘event’로 정의한다. 어렵게 설명했지만, 그냥 ‘user u가 음악 i를 좋아한다 좋아하지 않는다’에 대한 0, 1 값이다. 이 논문은 주어진 \(\Theta (x, y, b)\)에 대해 \(\ell\)이 1이 될 확률 \(pr_{ui}\)를 다음과 같이 logistic form으로 정의한다.

    + + +

    \[pr_{ui} := Pr[\ell_{ui} = 1 ~|~ \Theta] = \frac{\exp(x_u^\top y_i + b_u + b_i)}{1 + \exp(x_u^\top y_i + b_u + b_i)}. \]

    + + +

    직관적으로 생각해보았을 때, 앞선 논문 [2]은 r의 값이 0보다 크기만 하면 항상 u가 i를 좋아한다고 생각하지만, 이 논문에서는 그것이 r의 값에 대한 확률로 나타난다는 점을 알 수 있다. Reconstructed rating \(\hat r\)을 \(\hat r_{ui} = x_u^\top y_i\)이라고 했을 때, 위의 함수는 \(\hat r_{ui}\)에 대한 증가함수이므로 (\(\hat r_{ui}\)의 값이 \(-\infty\)가 되면 함수값이 0이고, \(\hat r_{ui}\) 값이 \(\infty\)가 되면 함수값이 1이 된다), 위의 식은 rating 값이 더 크면 호감을 가질 확률이 더 높아지는 형태의 확률 함수가 된다.

    + + +

    따라서 이 모델의 likelihood는 positive observation u,i의 set을 \(\mathcal S\)라 하였을 때 다음과 같이 쓸 수 있다.

    + + +

    \[\mathcal L_{naive} (R ~|~ \Theta) = \prod_{u,i \in \mathcal S} pr_{ui} \prod_{u,i \notin \mathcal S} (1-pr_{ui})\]

    + + +

    그러나 앞선 논문 [2]에서도 언급되었듯, negative feedback은 ‘싫어한다’와 다른 의미를 가지고 있기 때문에, 이 논문에서도 confidence라는 것을 정의하게 된다. [2]와의 차이점이라면 RMSE 관점이 아니라 앞에서 살펴본 likelihood function의 관점에서 정의를 한다는 점이다. 여기에서 \(c_{ui}\)는 \(\alpha r_{ui}\)로 사용하는데, 만약 hyper-parameter \(\alpha\)의 값이 크면 positive observation에 더 큰 비중을 두고, \(\alpha\)의 값이 작다면 negative observation에 더 큰 비중을 두는 식으로 다음과 같이 정의를 하게 된다.

    + + +

    \[ \mathcal L (R ~|~ \Theta) = \prod_{u,i} Pr[ \ell_{ui} | \Theta]^{\alpha r_{ui}} (1 - Pr[ \ell_{ui} | \Theta]).\]

    + + +

    위의 식에서 negative feedback에 대한 (즉, 만약 관측값 \(r_{ui}\)가 0이라면) likelihood 값은 \(\mathcal L (r_{ui} ~|~ \Theta ) = 1 - pr_{ui}\) 가 되므로 앞에서 계산한 naive한 likelihood function과 일치한다. 그러나 positive feedback에 대한 likelihood는 \(pr_{ui}^{\alpha r_{ui}} (1-pr_{ui}) \)가 되므로, \(\alpha\)의 값을 조절함에 따라 앞에서 계산한 값과 차이가 있다.

    + + +

    개인적인 생각으로는 여기에서 저자가 증명을 잘못한 것이 아닐까 생각된다. 만약 Positive feedback의 likelihood에 weight를 주기위해 exponent c를 추가한다고 했을 때, likelihood 식은 다음과 같이 된다.

    + + +

    \[ \mathcal L = \prod_{u,i \in \mathcal S} ( p_{ui}^{\ell_{ui}} (1-p_{ui})^{1-\ell_{ui}} )^{\alpha r_{ui}} \prod_{u,i \notin \mathcal S} p_{ui}^{\ell_{ui}} (1-p_{ui})^{1-\ell_{ui}}. \]

    + + +

    이때, \(r_{ui} > 0\) 일 때 \(\ell = 1\)이고, 혹은 둘 다 0이라는 특성을 잘 사용하면 이 식은 다음과 같이 표현이 된다.

    + + +

    \[\mathcal L = \prod_{u,i} p_{ui}^{\alpha r_{ui}} (1-p_{ui})^{1-\ell_{ui}}. \]

    + + +

    즉, 만약 저자가 의도한대로 positive feedback의 likelihood에 exponent로 weight를 주고 싶었다면, positive feedback의 likelihood function에서 1-p 부분이 없어야한다는 점이다. 이 부분은 저자가 실수를 했거나 혹은 내가 이해를 잘못했을 가능성이 있다. 혹시 몰라서 논문을 좀 찾아봤는데, 약 한 달 전쯤 나온 논문에서 증명한 결과가 내가 증명한 결과와 같은걸 보면, 저자가 틀린게 맞는 것 같다.

    + + +

    그리고 또 다른 문제는 \(pr_{ui}\)는 언제나 값이 0에서 1사이이기 때문에 \(c_{ui} = \alpha r_{ui}\)의 값이 크면 클수록 \(pr_{ui}\)의 값은 오히려 감소하게 된다는 것이다. 그래서 논문의 설명과는 반대로 \(\alpha\)의 값을 키우는 것이 오히려 positive observation의 weight를 낮추는 것이 아닌가하는 생각이 드는데, 논문을 여러 번 다시 읽어보고 계산을 해봐도 아직 아리송하다. 오히려 이렇게 하고 싶었다면, 최종 objective가 완전히 바뀌기는 하지만, 관측된 데이터 \(\mathcal S\)에 대해서 likelihood를 \( (1 + \alpha_{ui}) pr_{ui}\)와 같은 형태로 정의하는 편이 훨씬 좋지 않을까? 왜 base가 1보다 작은데, weight term을 exponent으로 올렸는지 이해가 되질 않는다. 이 부분은 혹시 나중에 이해가 완전히 되면 내용을 추가하도록 하겠다.

    + + +

    다시 본문으로 돌아오자. Likelihood를 계산했으니, prior만 있다면 posteriori를 계산할 수 있게 된다. 이 논문은 \(x, y\)의 prior를 전부 0 mean Normal distribution으로 정의한다. 이렇게 정의할 경우, 나중에 log MAP 문제를 풀게 될 때, L2 regularization과 같은 형태의 식 \(\frac{\lambda}{2} (\| x \|^2 + \| y \|^2) \) 을 얻을 수 있다. 자세한 증명은 생략한다. Prior를 정했으니 이제 posteriori를 구해서 다음과 같이 log MAP 문제를 정의할 수 있다. (논문에서 증명한 결과로, 내가 증명한 결과와는 차이가 있다)

    + + +

    \[ \log Pr[ \Theta | P ] = \sum_{u,i} c_{ui} \widehat r_{ui} - ( 1 + c_{ui} ) \log ( 1 + \exp \widehat r_{ui} ) - \frac{\lambda}{2} ( \| x_u\|^2 + \| y_i \|^2 ). \]

    + + +

    이 문제 역시 다른 문제들 처럼 한 번에 update하기가 어렵기 떄문에 alternative하게 update를 하게 된다. 정확히는 coordinate gradient method를 사용한다 (maximize문제이므로 ascent가 될 것이다). 여기에서 한 가지 문제가 발생하는데, 앞에 붙어있는 summation term이 모.든. (u,i) pair에 대한 summation이기 때문에 한 번 gradient를 계산하는 비용이 어마어마해진다는 것이다. 정확히는 아이템의 개수 I와 유저의 숫자 U에 대해 linear한 비용이 필요하다. 보통 그 둘의 값은 몇 백만, 몇 천만이 될 정도로 크기 때문에 scalability 이슈가 굉장히 중요해진다. 이 논문은 그런 문제를 해결하기 위하여 (속도가 느리다는 문제) AdaGradient를 사용하라거나, 전체에 대해 summation을 하는 대신, 전체 positive pair (u,i)와 일부 negative pair (u,i)만 사용해서 문제를 해결하라고 언급되어있다.

    + + +

    정리하자면 이 논문은 Matrix Factorization을 RMSE minimization 문제가 아닌 MAP 문제로 해결하려는 시도를 한 논문으로, MAP로 바꾸기 위하여 confidence가 포함된 likelihood function을 정의한다. (개인적으로는 이 likelihood function이 왜 이런 꼴을 하고 있는지 이해하지 못하였다) 알고리즘은 coordinate ascent를 사용하지만, 각각의 gradient 값이 아이템과 유저의 개수에 linear하기 때문에 실제 데이터에서 practical하지 못하다는 문제가 발생한다. 이런 문제를 해결하기 위하여 이 논문은 전체 matrix의 거의 대부분을 차지하는 negative observation을 전체 다 사용하는 대신, 일부만 sample하여 사용하는 방식을 제안하고 있다.

    + + + + +
    Bayesian Personalized Ranking for Non-Uniformly Sampled Items [4]
    + + +

    앞의 두 논문은 같은 문제의 objective function만 RMSE와 MAP로 서로 다르게 잡은 경우이지만, 이 논문은 앞의 방법들과 다소 다른 접근 방식을 취하고 있다. 이 논문은 먼저 선행 연구[5]를 조금 발전 시킨 논문인데, 선행 연구에서는 partially observed pair-wise competition 문제를 푸는 Baysian Personalized Ranking (BPR) optimization과 그것을 푸는 알고리즘을 제안하고, 그것을 MF로 확장하고있다. 그리고 그 다음 논문 [4]에서는 원래 논문이 가지는 단점을 negative observation을 adaptive하게 sample하는 방식으로 개선하고 있다.

    + + +

    먼저 핵심 notation들을 정의해보자. 관측된 (u,i) pair는 \(\mathcal S\)라는 set으로 정의된다. 여기에서 새로운 notation \(I_u^+\)와 \(U_i^+\) 2개가 introduce된다. \(I_u^+\)는 user u가 관측한 적 있는 item의 set이고, \(U_i^+\)는 item i를 관측한 적 있는 user u의 set이다. 그러면 이 set들을 통해 \(\mathcal D_S\)라는 triple을 다음과 같이 정의할 수 있다.

    + + +

    \[D_S := {(u,i,j) ~|~ i \in I_u^+ \mbox{ and } j \notin I_u^+ }.\]

    + + +

    즉, user u, user u가 관측한 item i와 관측하지 못한 item j 이렇게 셋의 triple인 것이다.

    + + +

    이제 이 논문의 핵심아이디어인 pair-wise ranking에 대해 살펴보자. 이 논문은 먼저 각각의 user u에게 item i와 item j간의 pair-wise ranking이 존재한다고 가정한다. 이 논문에서는 user u가 item i를 j보다 높은 order를 가질 때 \(i >_u j\)의 꼴로 표현한다. 이 pair-wise ranking은 (혹은 order는) totality, antisymmetry, transitivity를 만족하는 order로 정의된다. 자세한 수식은 논문에 있으니 생략한다.

    + + +

    이 논문은 user u가 item i를 item j보다 더 높게 평가할 확률을 다음과 같이 가정하고 있다.

    + + +

    \[Pr( i >_u j | \Theta) = \sigma(\hat r_{uij} (\Theta)).\]

    + + +

    여기에서 \(\hat r_{uij} := \hat r_{ui} - \hat r_{uj}\)로 정의되는 값이고, \(\sigma\)는 sigmoid function으로, \(\sigma(x) = \frac{1}{1+\exp(-x)}\)의 꼴로 정의된다. 각각의 user에 대해 pair-wise ranking에 대한 확률을 정의했고, 또한 실제 관측값도 있기 때문에, 우리는 ranking에 대한 likelihood를 정의할 수 있고, prior를 가정하게 되면 posteriori역시 계산할 수 있다. 먼저 likelihood \(\prod_u p(>_u | \Theta)\)부터 계산해보자. (이전과 마찬가지로 user들은 전부 independent하다고 가정했기 때문에 곱으로 표현된다)

    + + +

    \[\prod_u p(>_u | \Theta) = \prod_{u,i,j} \prod_u p( i >_u j | \Theta)^{I_{(u,i,j) \in \mathcal D_S}} (1 - p( i >_u j | \Theta) )^{I_{(u,i,j) \notin \mathcal D_S}} \]

    + + +

    \(I_x\)는 x가 true면 1이고 false면 0인 indicator function이다. 여기에서 order를 정의할 때 totality와 antisymmetry를 가정하였기 때문에, 위 식을 잘 건개하면 아래와 같은 간단한 식으로 표현하는 것이 가능하다고 한다.

    + + +

    \[\prod_u p(>_u | \Theta) = \prod_{(u,i,j) \in \mathcal D_S} p( i >_u j | \Theta). \]

    + + +

    [3]과 같이 prior를 zero mean normal distribution으로 정의하게 되면, log posteriori는 다음과 같이 표현된다.

    + + +

    \[ \max \ln Pr(\Theta | >_u) = \max \sum_{(u,i,j) \in \mathcal D_S} \ln \sigma(\hat x_{uij}) - \lambda \| \Theta \|^2. \]

    + + +

    이 문제는 stochastic gradient descent로 푸는 것이 가능하다. 자세한 미분 값은 논문에 있으니 생략한다. 참고로 이 논문은 이 문제를 optimization했을 때 얻을 수 있는 solution이 AUC (area under the ROC curve, ROC curve는 wiki 참고)를 maximization하는 것과 비슷한 문제라는 것을 따로 증명해두었으니 관심이 있다면 한 번쯤 읽어보면 좋을 것 같다.

    + + +

    [4]에서 제안한 BPR (baysian personalized ranking) problem을 풀기 위해서 SGD를 사용한다는 언급을 했는데, 문제가 하나 있다. 바로 triple \(\mathcal D_S\)의 개수가 너무 많아서 전부 uniform하게 뽑기에는 데이터가 너무너무 많다는 것이다. 그래서 [5]에서는 adapted sampling 방식을 제안하고 있다.

    + + +

    \[ \max \sum_{(u,i,j) \in \mathcal D_S} w_u w_i w_j \ln \sigma(\hat x_{uij}) - \lambda \| \Theta \|^2. \]

    + + +

    이때 \(w_u = 1/| I_u^+ |\), \(w_i = 1\)로 정의가 된다. 즉, 관측한 아이템이 더 많은 유저는 적게 뽑고, 모든 positivie item은 uniform하게 뽑는다. 마지막으로 \(w_j = \frac{1}{|U||I|} \sum_{u} I_{j \in I_u^+}\)으로 취하게 되는데, 더 많이 사용자들이 관측한, 혹은 좋아한 데이터 위주로 sample을 뽑는 방식이다.

    + + +

    정리해보면, BPR은 다른 논문들처럼 reconstructed error를 바로 measure하는 것이 아니라, pair-wise ranking을 정의하고, 복원된 rating \(\hat r_{ui}\)에 대해 user가 item i보다 j를 좋아할 확률을 sigmoid로 정의한다. 이 확률을 사용해 MAP문제를 정의하는데, 이 문제는 ROC curve의 넓이를 구하는 것과 비슷한 문제가 된다. 이때, sigmoid function을 step function으로 바꾸면 완전히 ROC curve의 넓이를 구하는 것과 같은 문제가 된다. Sigmoid가 step function의 가장 popular한 differentiable approximation 중 하나이기 때문에 sigmoid로 정의하게 되는 것이다. Algorithm은 SGD를 사용하는데, 데이터 셋이 user u가 관측한 item i와 관측하지 않은 item j의 triple이기 때문에 uniform sampling을 하게 되면 결과가 좋지 않을 수 있다. 때문에 adaptive하게 (u,i,j)에서 j 고를 때, popular한 j를 더 고르도록 sample을 하여 성능을 개선하고 있다.

    + + + + +
    정리
    + + +

    이 글에서는 Implicit feedback에 대해 recommendation을 어떻게 할 수 있을지 서로 다른 세 가지 접근방법을 소개했다. 첫 번째는 가장 기본적인 방법으로, confidence level \(c_{ui}\)를 정의하고, real value variable인 \(r_{ui}\)를 binary variable인 \(p_{ui}\)로 바꾼 다음 optimization을 푸는 방법에 대해 소개했다. 두 번째로는 RMSE를 optimization하는 대신, u가 i를 좋아할 확률을 모델링하고, 주어진 데이터에 대해 MAP를 푸는 방법에 대해 소개했다. 마지막으로는 각각의 user별로 item들끼리의 personalized pair-wise ranking을 정의하고, 역시 마찬가지로 u가 i보다 j를 좋아할 확률을 모델링하고 이것의 MAP를 구하는 방법에 대해 소개했다. 알고리즘은 SGD로 해결할 수 있지만, 조금 더 smart하게 item을 뽑는 adaptive sampling을 사용할 경우 성능이 더 올라간다고 한다.

    + + + + +
    변경 이력
    + + + + + +
    References
    + + +
      +
    1. Recommendation System (Matrix Completion)
    2. +
    3. Hu, Yifan, Yehuda Koren, and Chris Volinsky. “Collaborative filtering for implicit feedback datasets.”, 2008
    4. +
    5. Johnson, Christopher C. “Logistic matrix factorization for implicit feedback data.”, 2014
    6. +
    7. Gantner, Zeno, et al. “Bayesian personalized ranking for non-uniformly sampled items.”, 2012
    8. +
    9. Rendle, Steffen, et al. “BPR: Bayesian personalized ranking from implicit feedback.”, 2009
    10. +
    + + + + +
    + + +

    Machine Learning 스터디의 다른 글들

    + + + + +]]> + + <![CDATA[Machine learning 스터디 (17) Recommendation System (Matrix Completion)]]> @@ -33,7 +300,7 @@
    Matrix Completion
    -

    그러면 이제 추천 문제를 보다 엄밀하게 정의해보도록 하자. 먼저 데이터에 대해 살펴보도록 하자. 이 문제에서는 사용자와 상품이라는 두 가지 요소들이 존재한다. 사용자 \(u\)가 아이템 \(i\)를 얼마나 좋아할 것인지 나타내는 값을 rating \(r_{ui}\)라 하자. 이때, 이 rating은 Netflix처럼 1에서 5 사이의 real value일수도 있으며, 아마존이나 페이스북처럼 클릭했는지 하지 않았는지에 대한 데이터일수도 있다. 앞선 경우는 사용자가 자신이 얼마나 이 아이템을 좋아하는지 ‘명시적으로’ 나타냈기 때문에 explicit feedback이라 부르며, 후자의 경우는 사용자가 해당 상품을 좋아했는지 싫어했는지 표현을 직접적으로 하지 않으므로 ‘implicit feedback’이라고 부른다. 이에 대해서는 나중에 다른 글을 통해 더 자세히 다루도록 하겠다. 지금은 \(r_{ui}\)의 정확한 값을 알고 있고, 이 값이 전혀 noise가 없는 값이라고 가정하고 문제를 계속 설명하도록 하겠다. 이런 경우 우리가 가지고 있는 데이터는 \(r_{ui}\)들의 값이 될 것이고, 대략 아래와 같은 방식으로 matrix로 표현할 수 있을 것이다.

    +

    그러면 이제 추천 문제를 보다 엄밀하게 정의해보도록 하자. 먼저 데이터에 대해 살펴보도록 하자. 이 문제에서는 사용자와 상품이라는 두 가지 요소들이 존재한다. 사용자 \(u\)가 아이템 \(i\)를 얼마나 좋아할 것인지 나타내는 값을 rating \(r_{ui}\)라 하자. 이때, 이 rating은 Netflix처럼 1에서 5 사이의 real value일수도 있으며, 아마존이나 페이스북처럼 클릭했는지 하지 않았는지에 대한 데이터일수도 있다. 앞선 경우는 사용자가 자신이 얼마나 이 아이템을 좋아하는지 ‘명시적으로’ 나타냈기 때문에 explicit feedback이라 부르며, 후자의 경우는 사용자가 해당 상품을 좋아했는지 싫어했는지 표현을 직접적으로 하지 않으므로 ‘implicit feedback’이라고 부른다. 이에 대해서는 나중에 다른 글을 통해 더 자세히 다루도록 하겠다. (Implicit feedback에 대한 글을 새로 추가하였다) 지금은 \(r_{ui}\)의 정확한 값을 알고 있고, 이 값이 전혀 noise가 없는 값이라고 가정하고 문제를 계속 설명하도록 하겠다. 이런 경우 우리가 가지고 있는 데이터는 \(r_{ui}\)들의 값이 될 것이고, 대략 아래와 같은 방식으로 matrix로 표현할 수 있을 것이다.

    @@ -137,7 +404,7 @@

    이 pair-wise optimization 문제는 non-convex 문제이지만, gradient descent method로 local optimum에 수렴하는 결과를 얻을 수 있으며 실제로 꽤 효율적으로 좋은 결과를 얻을 수 있다.

    -

    또 다른 solver로는 Alternating Least Square (ALS) 라는 방법이 있다. 이 방법은 alternative하게 주어진 objective를 update하는 방법인데, 주어진 objective가 pairwise optimization으로 생각하면 non-convex이지만, p나 q 중 하나를 고정하고 나머지에 대해 optimization을 하게 되면 convex, 그것도 closed form으로 계산된다는 점을 이용한 방법이다. 따라서 이 방법을 사용해 예전에 설명했었던 k-means style의 알고리즘을 설계할 수 있는데, 이를 ALS라고 부르는 것이다. Gradient descent가 더 빠른 경우도 있지만, ALS를 사용하게 되면 각각의 element들이 다른 element에 independent하기 떄문에 분산처리가 간편하기 때문에 실제로는 ALS 방법도 많이 사용된다고 한다.

    +

    또 다른 solver로는 Alternating Least Square (ALS) 라는 방법이 있다. 이 방법은 alternative하게 주어진 objective를 update하는 방법인데, 주어진 objective가 pairwise optimization으로 생각하면 non-convex이지만, p나 q 중 하나를 고정하고 나머지에 대해 optimization을 하게 되면 convex, 그것도 closed form으로 계산된다는 점을 이용한 방법이다. 따라서 이 방법을 사용해 예전에 설명했었던 k-means style의 알고리즘을 설계할 수 있는데, 이를 ALS라고 부르는 것이다. Gradient descent가 더 빠른 경우도 있지만, ALS를 사용하게 되면 각각의 element들이 다른 element에 independent하기 때문에 분산처리가 간편하기 때문에 실제로는 ALS 방법도 많이 사용된다고 한다.

    지금까지 설명한 방법은 그야말로 가장 기본이 되는 모델이고, 이 모델을 조금 더 확장해보도록 하자. 가장 간단하게 확장할 수 있는 방법은 bias term을 추가하는 것이다. 예를 들어서 어떤 user는 항상 모든 영화 평점을 비교적 ‘짜게’ 주는 user가 있을 수 있고, 반대로 모든 영화에 점수를 후하게 주는 user도 있을 수 있다. 어떤 영화는 개봉 전부터 평단이나 기자들에게서 호평을 받았거나 유명 배우가 나와 기본 점수가 높을 수도 있고, 그 반대도 가능하다. 따라서 이런 현상들을 반영할 수 있는 bias term이 추가가 되는 것은 지극히 자연스럽다고 할 수 있다. user의 bias를 \(b_u\), item의 bias를 \(b_i\)라고 하면 (이 값들은 vector가 아니라 scalar value이다) user u의 item i에 대한 bias \(b_{ui}\)는 \(b_{ui} = \mu + b_u + b_i\)로 표현할 수 있을 것이다. 여기에서 \(\mu\)는 전체 모든 r_{ui}의 평균 값으로, bias라는 개념이 평균에서부터 얼마나 멀어지는 가에 대한 개념이므로 평균 값도 함께 고려하는 것이다. \(\mu\)는 데이터와 함께 주어지는 값이고, bias term들은 p,q처럼 optimization을 통해 찾아야하는 optimization parameter가 된다. 이를 사용하면 reconstructed rating \(\hat r_{ui}\)는 다음과 같이 표현된다.

    @@ -179,7 +446,7 @@
    정리
    -

    이 글에서는 recommendation 문제가 어떤 문제인지 설명하고, 보다 수학적으로 정의된 matrix completion 문제로 recommendation을 설명한다. 그 후 이 문제를 푸는 가장 popular한 방법인 matrix factorization에 대해서 다룬다. 해당 문제가 non-convex 문제이기 떄문에 convex relaxation을 통해 문제를 푸는 방법 (더 늦게 나온 방법이다), non-convex optimization을 바로 푸는 방법 (Netflix prize에서 실제 사용했던 방법) 두 가지를 소개하고 각각에 대해 간략하게 설명한다. 실제 recommendation은 matrix factorization 뿐 아니라 여러 다른 methodology들을 결합해서 문제를 풀게 되지만, 여전히 단일 model로 가장 좋은 performance를 보여주는 것은 matrix factorization을 기반으로 한 방법론들이기 떄문에 matrix factorization을 제대로 아는 것이 recommendation 문제를 풀기 위한 첫 걸음이라 할 수 있을 것이다.

    +

    이 글에서는 recommendation 문제가 어떤 문제인지 설명하고, 보다 수학적으로 정의된 matrix completion 문제로 recommendation을 설명한다. 그 후 이 문제를 푸는 가장 popular한 방법인 matrix factorization에 대해서 다룬다. 해당 문제가 non-convex 문제이기 때문에 convex relaxation을 통해 문제를 푸는 방법 (더 늦게 나온 방법이다), non-convex optimization을 바로 푸는 방법 (Netflix prize에서 실제 사용했던 방법) 두 가지를 소개하고 각각에 대해 간략하게 설명한다. 실제 recommendation은 matrix factorization 뿐 아니라 여러 다른 methodology들을 결합해서 문제를 풀게 되지만, 여전히 단일 model로 가장 좋은 performance를 보여주는 것은 matrix factorization을 기반으로 한 방법론들이기 때문에 matrix factorization을 제대로 아는 것이 recommendation 문제를 풀기 위한 첫 걸음이라 할 수 있을 것이다.

    @@ -195,13 +462,13 @@
    References
    - +
      +
    1. 인터넷 속의 수학 - How Does Netflix Recommend Movies? (1/2)
    2. +
    3. 인터넷 속의 수학 - How Does Netflix Recommend Movies? (2/2)
    4. +
    5. Kennedy, Ryan. “Low-rank matrix completion.”, 2013
    6. +
    7. Candès, Emmanuel J., and Benjamin Recht. “Exact matrix completion via convex optimization.”, 2009
    8. +
    9. Koren, Yehuda, Robert Bell, and Chris Volinsky. “Matrix factorization techniques for recommender systems.”, 2009
    10. +
    @@ -230,10 +497,20 @@
  • EM algorithm
  • Hidden Markov Model
  • Dimensionality Reduction (LDA, PCA)
  • -
  • Recommendation System (Matrix Completion)
  • +
  • Recommendation System (Matrix Completion) + + +
  • Neural Network Introduction
  • Deep Learning 1 – RBM, DNN, CNN
  • -
  • Reinforcement Learning
  • +
  • Reinforcement Learning + +
      +
    • Multi-armed Bandit
    • +
    +
  • ]]> @@ -3446,34 +3723,6 @@

    사용 방법은 심플하다. http://www.disqus.com 에 접속해서 아이디 만들고, 새로 disqus 하나 만든 다음에, 옥토프레스 _config.yml에 disqus short name만 넣어주면 된다.

    -]]> -
    - - - <![CDATA[맥 요세미티 업데이트 이후 Homebrew 문제점 troubleshooting]]> - - 2014-11-30T03:24:00+09:00 - http://SanghyukChun.github.io/81 - 나는 맥 OS 장비가 두 개 있다. 하나는 연구실에서 사용하는 아이맥이고 또 하나는 연구실 밖에서 사용하는 맥북프로이다. 이번 요세미티 업데이트는 프리뷰 때 부터 기대를 많이 했기에 나오자마자 바로 두 머신 모두 요세미티 설치를 했다. 그리고 그게 내가 이 글을 쓰게 된 시발점이 되었다.

    - - -

    Homebrew는 맥에서 가장 많이 사용하는 패키지 관리 프로그램 중 하나로, 간단히 생각하면 ubuntu의 apt-get과 같은 역할을 (더 fancy하게!) 해준다. 그런데 문제는 이 녀석이 ruby base로 돌아가고, ruby path가 하드코딩되어있는데, 요세미티는 system ruby의 버전을 강제로 업그레이드시켜버리기 때문에 brew의 모든 명령이 깨진다는 것. 참고로 이건 예전 버전의 homebrew에서나 그렇고, 새로 나온 버전은 아무 문제가 없다.

    - - -

    가장 간단한 해결책은 임시로 brew.rb 파일에 있는 path를 current ruby로 바꿔준 다음 brew를 최신 버전으로 다시 내려받는 것이다. 하지만 늘 인생은 쉽지 않지. 쉬운 길을 눈 앞에 두고 돌아가기 마련인데, 두 머신 모두 (약간의 시간 차이를 두고 한 일이지만) brew를 삭제하고, 다시 설치한다음 rvm으로 ruby 버전을 다시 다운로드 받는… 삽질을 했다.

    - - -

    그런데 이번에 rvm으로 루비 버전을 먼저 내려받다가, brew가 깨져있는 상황에서 실수로 지금 이미 있는 루비조차 날려버리는 최악의 실수를 해버렸다. 다시 말해 ruby 를 입력해도 아무 반응이 없고, rvm list는 비어있는 최악의 상황. 먼저 rvm install 2.0.0 을 실행했는데, 컴파일이 거의 한 시간 정도의 긴 시간이 지나도 끝나지를 않아 찾아보니 xcode 버전이 낮으면 그럴 수 있다더라. 다시 내 xcode를 보니 5버전.. 최신은 6버전이다. 바로 xcode부터 재설치를 했다. 근데 xcode가 보통 무거워야지.. 설치하는데 시간이 꽤 걸렸다.

    - - -

    xcode 재설치를 끝내고 homebrew를 설치하려고 보니 ruby 명령어를 실행해야하는터라, 강제로 system ruby path에 들어가 system ruby로 실행을 시켰다. brew를 제대로 지우지 않은 상황이라면 시키는대로 하면 된다. 설치 후에는 brew doctor 한 번 돌려줘야한다. 꼬여있는 dependency를 정리해야하기 때문.

    - - -

    이제 rvm install 2.0.0, rvm install 1.9.3, rvm install 2.1.5 모두 잘 실행된다. (라고 적었지만 사실 1.9.3은 gcc48설치하는게 너무 오래 걸려서 아직 끝은 안난게 함정…)

    - - -

    Gitlab도 그렇고, 지금 쓰고 있는 Octopress도 그렇고, 많은 웹에서 쓰는 프레임워크 혹은 어플리케이션들이 rails 기반, 루비 기반인 경우가 많아 이런 문제가 왕왕 생기고는 하는데, 당황하지 않고 천천히 찾아보면 해결하는게 크게 어렵지는 않다. 루비는 sudo user로 설치하는게 아니다보니까 더 쉬운 듯. 가장 중요한건 인내심을 가지는 것. 너무 이상할 정도로 오래걸리는건 생각해볼 필요가 있지만, 대부분의 경우 생각보다 시간이 훨씬 오래 걸린다.

    - ]]>
    diff --git a/blog/categories/big-data/atom.xml b/blog/categories/big-data/atom.xml index 658432c1..35020c83 100644 --- a/blog/categories/big-data/atom.xml +++ b/blog/categories/big-data/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Big-Data | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/big2014/atom.xml b/blog/categories/big2014/atom.xml index a86b8214..a8252a73 100644 --- a/blog/categories/big2014/atom.xml +++ b/blog/categories/big2014/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Big2014 | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/book/atom.xml b/blog/categories/book/atom.xml index a8864b7f..40c36c8d 100644 --- a/blog/categories/book/atom.xml +++ b/blog/categories/book/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Book | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/bootstrap/atom.xml b/blog/categories/bootstrap/atom.xml index 5bb5b596..fa9fda6d 100644 --- a/blog/categories/bootstrap/atom.xml +++ b/blog/categories/bootstrap/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: bootstrap | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/common/atom.xml b/blog/categories/common/atom.xml index 6fc25afd..78fb6853 100644 --- a/blog/categories/common/atom.xml +++ b/blog/categories/common/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Common | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/conference/atom.xml b/blog/categories/conference/atom.xml index d737d8a1..13402d07 100644 --- a/blog/categories/conference/atom.xml +++ b/blog/categories/conference/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Conference | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/cousera-nn/atom.xml b/blog/categories/cousera-nn/atom.xml index 8bf5fdd2..faa9b077 100644 --- a/blog/categories/cousera-nn/atom.xml +++ b/blog/categories/cousera-nn/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Cousera-NN | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/deep-learning/atom.xml b/blog/categories/deep-learning/atom.xml index 659e67e9..74cec9e4 100644 --- a/blog/categories/deep-learning/atom.xml +++ b/blog/categories/deep-learning/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Deep-Learning | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/development/atom.xml b/blog/categories/development/atom.xml index eaf1b2c8..0993a4e5 100644 --- a/blog/categories/development/atom.xml +++ b/blog/categories/development/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Development | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/facebook/atom.xml b/blog/categories/facebook/atom.xml index 47c0d619..5e13adce 100644 --- a/blog/categories/facebook/atom.xml +++ b/blog/categories/facebook/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Facebook | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/game/atom.xml b/blog/categories/game/atom.xml index a4bc4202..23323c59 100644 --- a/blog/categories/game/atom.xml +++ b/blog/categories/game/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Game | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/gitlab/atom.xml b/blog/categories/gitlab/atom.xml index 80407430..8991ae00 100644 --- a/blog/categories/gitlab/atom.xml +++ b/blog/categories/gitlab/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: gitlab | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/icml/atom.xml b/blog/categories/icml/atom.xml index 42b65a60..76e816c9 100644 --- a/blog/categories/icml/atom.xml +++ b/blog/categories/icml/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: ICML | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/icml2014/atom.xml b/blog/categories/icml2014/atom.xml index 8a40cbdd..6aa17455 100644 --- a/blog/categories/icml2014/atom.xml +++ b/blog/categories/icml2014/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: ICML2014 | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/jounals/atom.xml b/blog/categories/jounals/atom.xml index 486a401a..a4154c49 100644 --- a/blog/categories/jounals/atom.xml +++ b/blog/categories/jounals/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Jounals | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/kaggle/atom.xml b/blog/categories/kaggle/atom.xml index 8e9f65aa..88ed618d 100644 --- a/blog/categories/kaggle/atom.xml +++ b/blog/categories/kaggle/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Kaggle | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/kaist/atom.xml b/blog/categories/kaist/atom.xml index 95b744e6..4d3a01a2 100644 --- a/blog/categories/kaist/atom.xml +++ b/blog/categories/kaist/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: KAIST | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/lecture/atom.xml b/blog/categories/lecture/atom.xml index ceff3d23..a47860ad 100644 --- a/blog/categories/lecture/atom.xml +++ b/blog/categories/lecture/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Lecture | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/machine-learning-study/atom.xml b/blog/categories/machine-learning-study/atom.xml index b9f84098..e9ccc4f7 100644 --- a/blog/categories/machine-learning-study/atom.xml +++ b/blog/categories/machine-learning-study/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Machine-Learning-Study | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ @@ -13,6 +13,273 @@ Octopress + + <![CDATA[Machine learning 스터디 (17-1) Recommendation System with Implicit Feedback]]> + + 2016-03-06T01:30:00+09:00 + http://SanghyukChun.github.io/95 + 들어가며 + + +

    이전 글[1]에서 다룬 recommendation system은 사용자가 점수를 정확하게 매긴 경우에 대해, 즉 explicit feedback에 대해서만 문제를 푸는 방식이다. 그러나 실제로는 사용자가 점수를 직접 매기는 대신에 단순히 클릭했거나, 조회, 구매한 간접적인 정보, 즉 implicit feedback에만 의존하게 되는 경우도 빈번하게 발생하게 된다. 이 글에서는 그런 implicit feedback만 존재하는 상황에서 어떻게 matrix completion 문제를 디자인하고 해결하는지에 대해 총 세 개의 논문을 들어 설명할 것이다.

    + + +

    이 글의 맨 앞은 implicit feedback이 정확히 무엇이고, 어떤 상황의 문제들이 있는지에 대해 설명할 것이다. 그리고 총 세 개의 논문에서 어떤 방법으로 문제를 접근하는가 설명하도록 할 것이다. 소개할 논문은 Collaborative Filtering for Implicit Feedback Datasets [2], Logistic Matrix Factorization for Implicit Feedback Data [3], Bayesian Personalized Ranking for Non-Uniformly Sampled Items [4] 총 세가지이다.

    + + + + +
    Explicit Feedback and Implicit Feedback
    + + +

    본격적으로 논문들이 제안한 방법론을 살펴보기 전에, explicit feedback과 implicit feedback의 차이점에 대해 논해보도록 하겠다. 먼저 explicit feedback은 사용자가 정확하게 본인이 얼마나 이 item에 호감이 있는지를 수치로 feedback을 주는 것이다. 예를 들어서 Netflix problem의 별점 데이터는 정확하게 1점부터 5점 사이의 well-define된 범위를 가진다. 그러나 실제로는 사용자의 item에 대한 정확한 호감도를 요구하는 것이 어려울 때가 있다. 아마존의 상품 추천을 예로 들어보자. 아마존이 가질 수 있는 데이터는 사용자가 어떤 상품들을 조회하였는지와, 어떤 상품들을 구매하였는지 정도의 정보 밖에 가질 수 없다. 이 경우 사용자가 살펴본 물건들이 사용자가 정말 매력있게 느껴서 살펴본 것인지, 만약 그렇다면 얼마만큼의 호감도가 있는지 판별하는 것이 매우 어려울 것이다. 이런 종류의 데이터를 사용자가 직접적인 점수를 주는 대신 간접적인 정보만을 제공한다고 하여 implicit feedback이라 부른다. 이외에도 sound cloud나 youTube 등에서 사용자가 재생한 재생목록이나 반복하여 청취 혹은 시청한 횟수 등의 데이터도 마찬가지로 implicit feedback의 대표적인 예가 될 수 있다.

    + + +

    Implicit feedback에서 관측 값은 click이나 재생 횟수 (0 혹은 0보다 큰 정수) 일 수도 있고, 음악 등의 item을 재생한 총 시간 (0 이상의 실수) 일 수도 있다. 한 가지 주의할 점은, explicit feedback처럼 사용자가 구체적으로 item에 대한 preference를 제공하지 않기 떄문에 사용자가 선호하지 않아서 선택하지 않은 item과 아직 관측하지 않은, 그러나 잠재적으로 흥미가 있는 item 모두 값이 0일 것이라는 점이다. 보통 사용자들이 item을 굉장히 조금만 click하거나 (뉴스) 사용하거나 (음악, 동영상) 구매하기 때문에 (쇼핑) 실제로는 matrix의 거의 대부분이 비어있고 아주 일부분의 데이터만 관측되기 때문에, negative observation이 positive observation의 수를 압도한게 되고, 때문에 이런 점을 고려하지 않고 모델을 설계하게 되면 아주 일부분의 positive 데이터와 거의 대부분의 negative observation (값이 0인 observation)들에의해 model이 overfitting된다. 또한, implicit feedback은 굉장히 노이즈가 많기 때문에 주어진 데이터를 얼마나 믿을 수 있을지 알 수 없다는 것이다. 예를 들어서 사용자가 물건을 하나 구매하였더라도, 이 물건에 대해 반드시 긍정적으로 생각할 것이라 기대할 수는 없을 것이다. 가끔은 구매한 물건이 아주 불만족스럽고 다시는 비슷한 물건을 구매하고 싶지 않을 수도 있지 않은가? 이런 두 가지 이슈 (negative observation, confidence)는 recommendation model이 implicit feedback을 처리하기 위해 반드시 고려되어야 할 이슈가 된다.

    + + + + +
    Recall: Matrix Factorization with Explicit Feedback
    + + +

    이전 글 [1]에서 다뤘던 objective function은 다음과 같다.

    + + +

    \[\min_{X,Y} \sum_{u,i \in \kappa} (r_{ui} - x_u^\top y_i)^2 + \lambda ( \| x_u \|_2^2 + \| y_i \|_2^2 ) .\]

    + + +

    이전 글에서는 x,y 대신 p,q noataion을 사용했으나 이 글에서는 전부 x, y notation으로 통일하도록 하겠다. 이 objective에 대해서는 이전 글을 참고하면서 읽으면 좋을 것 같다. 원래는 bias term까지 포함해야하지만, 이 글에서는 편의상 bias term은 생략하도록 하겠다. 이 문제는 SGD (Stochastic Gradient Descent), ALS (Alternating Least Square) 등의 solver를 사용해 풀 수 있으며 이 글에서는 자세한 설명을 생략하도록 하겠다.

    + + + + +
    Collaborative Filtering for Implicit Feedback Datasets [2]
    + + +

    Implicit feedback을 처리하는 가장 기본적인 접근법을 소개해보자. 이 논문은 user u가 item i를 선호하는지 하지 않는지 여부를 가르키는 preference vector \(p_{ui}\)를 정의한다. \(p_{ui}\)의 값은 \(sign(r_{ui})\)으로 정의할 수 있다. sign 함수는 input 값의 'sign'을 return하는 함수로, 즉 input이 negative value면 -1, positive value면 1을 return한다. 따라서 perference의 값은 rating r이 0보다 크다면 1이고, r이 0이라면 p도 0이 되는 것이다.

    + + +

    앞서 설명한 것 처럼, preference vector의 값을 항상 신뢰할 수 있는 것은 아니다. 때문에, 이 논문에서는 confience level \(c_{ui}\)라는 것을 정의하게 된다. 우리가 한 가지 가정할 수 있는 것은 만약 \(r_{ui}\)의 값이 크다면, 예를 들어 한 사용자가 한 항목을 엄청 많이 재구매했다거나 한다면, u는 i를 아주 높은 확률로 prefer한다는 사실을 가정할 수 있다. 따라서 confidence level은 r에 대한 increasing function으로 정의하는 것이 타당하다고 할 수 있다. 이 논문에서는 confidence level \(c_{ui}\)를 다양한 방식으로 정의할 수 있다고 언급하고 있으며, 실제 실험에서는 가장 직관적이고 단순한 increasing function은 linear function을 사용한다. 따라서 이 논문에서는 다음과 같은 confidence를 사용한다.

    + + +

    \[c_{ui} = 1 + \alpha r_{ui}.\]

    + + +

    혹은 \(c_{ui} = 1 + \alpha \log (1 + r_{ui}/ \varepsilon)\) 등의 confidence도 대안으로 제안하기는 하지만, 기본적으로 위에서 설명한 linear confidence를 사용하는 듯하다. 한 가지 짚고 넘어가야할 점은, \(c_{ui}\)는 실제 데이터 \(r_{ui}\)와 hyper-parameter \(\alpha\)에 의해서만 정의되므로 optimize해야 할 parameter가 아니라 한 번 confidence를 정의하기만하면 고정되는 constant라는 점이다. 따라서 confidence의 값을 어떻게 정의하더라도 전체 알고리즘의 로직을 바꾸거나 하지는 않는다.

    + + +

    c를 정의하는 것에는 또 하나의 이점이 있다. Parameter \(\alpha\)가 positive observation과 negative observation의 중요도를 조절하는 역할을 하게 되면서, negative feedback에 대한 중요도를 조절할 수 있는 것이다. 예를 들어 \(\alpha\)의 크기가 작다면, positive와 negative observation의 confidence 차이가 큰 \(\alpha\)를 가질 때 보다 상대적으로 작을 것이라는 것을 기대할 수 있게 된다.

    + + +

    이제 objective를 정의할 차례이다. Explicit feedback에서는 복원한 rating \(\hat r_{ui}\)와 관측한 데이터 \(r_{ui}\)의 RMSE를 바로 계산하였으나, 앞서 말한대로 이 값을 그대로 계산하기에는 confidence의 문제가 있다. 이 논문에서는 앞서 정의한 confidence를 고려하여 objective function은 다음과 같이 정의한다.

    + + +

    \[\min_{X,Y} \sum_{u,i \in \kappa} c_{ui}(p_{ui} - x_u^\top y_i)^2 + \lambda ( \| x_u \|_2^2 + \| y_i \|_2^2 ) .\]

    + + +

    맨 처음 objective와 달라진 점은, rating vector r (0 이상의 real value) 이 preference vector p (0 또는 1) 로 바뀌었다는 점과, 각각의 u,i pair에 대해 confidence \(c_{ui}\)가 곱해진다는 점이다. 이때, \(c_{ui}\)는 optimization parameter와는 상관없이 맨 처음 정해지고 변하지 않는 값이므로, 이렇게 바뀐 objective function을 풀기 위해서 이전 문제와 마찬가지로 살짝 변형된 SGD나 ALS 등을 사용할 수 있다. 논문에서는 조금 더 scalability를 고려한 방법론을 제안하는데, matrix product를 조금 더 효율적으로 하도록 matrix들을 decompose하여 조금 더 order가 낮은 연산을 하는 방법을 사용한다. 자세한 알고리즘은 논문을 참고하면 좋을 것 같다.

    + + +

    정리하자면 이 논문은 rating vector r을 preference vector p로 변환하고, confidence level c를 정의한 후, p와 c를 사용해 RMSE objective function을 optimize하는 work인 것이다. 그리고 앞서 설명했던 두 가지 문제는 confidence level c를 정의하는 방법에 의해 해결할 수 있다.

    + + + + +
    Logistic Matrix Factorization for Implicit Feedback Data [3]
    + + +

    앞선 논문 [2]에서는 RMSE를 minimize하는 objective를 설계하였지만, RMSE가 아닌 다른 형태의 objective를 optimize하는 것도 가능하다. 앞선 논문에서 RMSE를 minimize함으로써 얻을 수 있는 효과는 관측한 preference \(p_{ui}\)와 복원한 preference \(\hat p_{ui}\)가 서로 (RMSE의 관점에서) 최대한 비슷한 값을 가지도록 optimization이 된다는 것이다. 이 논문은 perference의 RMSE를 minimize하는 문제 대신, 관측 값 \(r\)과 optimization parameter \(\Theta = (x, y, b)\) 등의 posteriori를 maximization하는 방식을 취한다. 참고로 이 논문은 Spotify에서 발표한 논문으로, 실제 음악 추천에서 응용하고 있는 듯 하다.

    + + +

    계속 강조하듯, 여기에서 실제 유저가 선호하는 것과 유저의 implicit feedback 결과는 다를 수 있다. 그렇기 때문에 이 논문은 u가 i를 좋아할 확률을 logistic function으로 확률적으로 정의한 후, 관측한 데이터로부터 posteriori를 maximize하는 방향으로 학습을 하게 된다. 그러기위해 이 논문은 \(\ell_{ui}\)이라는 새로운 notation을 introduce하고 이를 사용자 u가 item i를 선호하는 'event'로 정의한다. 어렵게 설명했지만, 그냥 'user u가 음악 i를 좋아한다 좋아하지 않는다'에 대한 0, 1 값이다. 이 논문은 주어진 \(\Theta (x, y, b)\)에 대해 \(\ell\)이 1이 될 확률 \(pr_{ui}\)를 다음과 같이 logistic form으로 정의한다.

    + + +

    \[pr_{ui} := Pr[\ell_{ui} = 1 ~|~ \Theta] = \frac{\exp(x_u^\top y_i + b_u + b_i)}{1 + \exp(x_u^\top y_i + b_u + b_i)}. \]

    + + +

    직관적으로 생각해보았을 때, 앞선 논문 [2]은 r의 값이 0보다 크기만 하면 항상 u가 i를 좋아한다고 생각하지만, 이 논문에서는 그것이 r의 값에 대한 확률로 나타난다는 점을 알 수 있다. Reconstructed rating \(\hat r\)을 \(\hat r_{ui} = x_u^\top y_i\)이라고 했을 때, 위의 함수는 \(\hat r_{ui}\)에 대한 증가함수이므로 (\(\hat r_{ui}\)의 값이 \(-\infty\)가 되면 함수값이 0이고, \(\hat r_{ui}\) 값이 \(\infty\)가 되면 함수값이 1이 된다), 위의 식은 rating 값이 더 크면 호감을 가질 확률이 더 높아지는 형태의 확률 함수가 된다.

    + + +

    따라서 이 모델의 likelihood는 positive observation u,i의 set을 \(\mathcal S\)라 하였을 때 다음과 같이 쓸 수 있다.

    + + +

    \[\mathcal L_{naive} (R ~|~ \Theta) = \prod_{u,i \in \mathcal S} pr_{ui} \prod_{u,i \notin \mathcal S} (1-pr_{ui})\]

    + + +

    그러나 앞선 논문 [2]에서도 언급되었듯, negative feedback은 '싫어한다'와 다른 의미를 가지고 있기 때문에, 이 논문에서도 confidence라는 것을 정의하게 된다. [2]와의 차이점이라면 RMSE 관점이 아니라 앞에서 살펴본 likelihood function의 관점에서 정의를 한다는 점이다. 여기에서 \(c_{ui}\)는 \(\alpha r_{ui}\)로 사용하는데, 만약 hyper-parameter \(\alpha\)의 값이 크면 positive observation에 더 큰 비중을 두고, \(\alpha\)의 값이 작다면 negative observation에 더 큰 비중을 두는 식으로 다음과 같이 정의를 하게 된다.

    + + +

    \[ \mathcal L (R ~|~ \Theta) = \prod_{u,i} Pr[ \ell_{ui} | \Theta]^{\alpha r_{ui}} (1 - Pr[ \ell_{ui} | \Theta]).\]

    + + +

    위의 식에서 negative feedback에 대한 (즉, 만약 관측값 \(r_{ui}\)가 0이라면) likelihood 값은 \(\mathcal L (r_{ui} ~|~ \Theta ) = 1 - pr_{ui}\) 가 되므로 앞에서 계산한 naive한 likelihood function과 일치한다. 그러나 positive feedback에 대한 likelihood는 \(pr_{ui}^{\alpha r_{ui}} (1-pr_{ui}) \)가 되므로, \(\alpha\)의 값을 조절함에 따라 앞에서 계산한 값과 차이가 있다.

    + + +

    개인적인 생각으로는 여기에서 저자가 증명을 잘못한 것이 아닐까 생각된다. 만약 Positive feedback의 likelihood에 weight를 주기위해 exponent c를 추가한다고 했을 때, likelihood 식은 다음과 같이 된다.

    + + +

    \[ \mathcal L = \prod_{u,i \in \mathcal S} ( p_{ui}^{\ell_{ui}} (1-p_{ui})^{1-\ell_{ui}} )^{\alpha r_{ui}} \prod_{u,i \notin \mathcal S} p_{ui}^{\ell_{ui}} (1-p_{ui})^{1-\ell_{ui}}. \]

    + + +

    이때, \(r_{ui} > 0\) 일 때 \(\ell = 1\)이고, 혹은 둘 다 0이라는 특성을 잘 사용하면 이 식은 다음과 같이 표현이 된다.

    + + +

    \[\mathcal L = \prod_{u,i} p_{ui}^{\alpha r_{ui}} (1-p_{ui})^{1-\ell_{ui}}. \]

    + + +

    즉, 만약 저자가 의도한대로 positive feedback의 likelihood에 exponent로 weight를 주고 싶었다면, positive feedback의 likelihood function에서 1-p 부분이 없어야한다는 점이다. 이 부분은 저자가 실수를 했거나 혹은 내가 이해를 잘못했을 가능성이 있다. 혹시 몰라서 논문을 좀 찾아봤는데, 약 한 달 전쯤 나온 논문에서 증명한 결과가 내가 증명한 결과와 같은걸 보면, 저자가 틀린게 맞는 것 같다.

    + + +

    그리고 또 다른 문제는 \(pr_{ui}\)는 언제나 값이 0에서 1사이이기 때문에 \(c_{ui} = \alpha r_{ui}\)의 값이 크면 클수록 \(pr_{ui}\)의 값은 오히려 감소하게 된다는 것이다. 그래서 논문의 설명과는 반대로 \(\alpha\)의 값을 키우는 것이 오히려 positive observation의 weight를 낮추는 것이 아닌가하는 생각이 드는데, 논문을 여러 번 다시 읽어보고 계산을 해봐도 아직 아리송하다. 오히려 이렇게 하고 싶었다면, 최종 objective가 완전히 바뀌기는 하지만, 관측된 데이터 \(\mathcal S\)에 대해서 likelihood를 \( (1 + \alpha_{ui}) pr_{ui}\)와 같은 형태로 정의하는 편이 훨씬 좋지 않을까? 왜 base가 1보다 작은데, weight term을 exponent으로 올렸는지 이해가 되질 않는다. 이 부분은 혹시 나중에 이해가 완전히 되면 내용을 추가하도록 하겠다.

    + + +

    다시 본문으로 돌아오자. Likelihood를 계산했으니, prior만 있다면 posteriori를 계산할 수 있게 된다. 이 논문은 \(x, y\)의 prior를 전부 0 mean Normal distribution으로 정의한다. 이렇게 정의할 경우, 나중에 log MAP 문제를 풀게 될 때, L2 regularization과 같은 형태의 식 \(\frac{\lambda}{2} (\| x \|^2 + \| y \|^2) \) 을 얻을 수 있다. 자세한 증명은 생략한다. Prior를 정했으니 이제 posteriori를 구해서 다음과 같이 log MAP 문제를 정의할 수 있다. (논문에서 증명한 결과로, 내가 증명한 결과와는 차이가 있다)

    + + +

    \[ \log Pr[ \Theta | P ] = \sum_{u,i} c_{ui} \widehat r_{ui} - ( 1 + c_{ui} ) \log ( 1 + \exp \widehat r_{ui} ) - \frac{\lambda}{2} ( \| x_u\|^2 + \| y_i \|^2 ). \]

    + + +

    이 문제 역시 다른 문제들 처럼 한 번에 update하기가 어렵기 떄문에 alternative하게 update를 하게 된다. 정확히는 coordinate gradient method를 사용한다 (maximize문제이므로 ascent가 될 것이다). 여기에서 한 가지 문제가 발생하는데, 앞에 붙어있는 summation term이 모.든. (u,i) pair에 대한 summation이기 때문에 한 번 gradient를 계산하는 비용이 어마어마해진다는 것이다. 정확히는 아이템의 개수 I와 유저의 숫자 U에 대해 linear한 비용이 필요하다. 보통 그 둘의 값은 몇 백만, 몇 천만이 될 정도로 크기 때문에 scalability 이슈가 굉장히 중요해진다. 이 논문은 그런 문제를 해결하기 위하여 (속도가 느리다는 문제) AdaGradient를 사용하라거나, 전체에 대해 summation을 하는 대신, 전체 positive pair (u,i)와 일부 negative pair (u,i)만 사용해서 문제를 해결하라고 언급되어있다.

    + + +

    정리하자면 이 논문은 Matrix Factorization을 RMSE minimization 문제가 아닌 MAP 문제로 해결하려는 시도를 한 논문으로, MAP로 바꾸기 위하여 confidence가 포함된 likelihood function을 정의한다. (개인적으로는 이 likelihood function이 왜 이런 꼴을 하고 있는지 이해하지 못하였다) 알고리즘은 coordinate ascent를 사용하지만, 각각의 gradient 값이 아이템과 유저의 개수에 linear하기 때문에 실제 데이터에서 practical하지 못하다는 문제가 발생한다. 이런 문제를 해결하기 위하여 이 논문은 전체 matrix의 거의 대부분을 차지하는 negative observation을 전체 다 사용하는 대신, 일부만 sample하여 사용하는 방식을 제안하고 있다.

    + + + + +
    Bayesian Personalized Ranking for Non-Uniformly Sampled Items [4]
    + + +

    앞의 두 논문은 같은 문제의 objective function만 RMSE와 MAP로 서로 다르게 잡은 경우이지만, 이 논문은 앞의 방법들과 다소 다른 접근 방식을 취하고 있다. 이 논문은 먼저 선행 연구[5]를 조금 발전 시킨 논문인데, 선행 연구에서는 partially observed pair-wise competition 문제를 푸는 Baysian Personalized Ranking (BPR) optimization과 그것을 푸는 알고리즘을 제안하고, 그것을 MF로 확장하고있다. 그리고 그 다음 논문 [4]에서는 원래 논문이 가지는 단점을 negative observation을 adaptive하게 sample하는 방식으로 개선하고 있다.

    + + +

    먼저 핵심 notation들을 정의해보자. 관측된 (u,i) pair는 \(\mathcal S\)라는 set으로 정의된다. 여기에서 새로운 notation \(I_u^+\)와 \(U_i^+\) 2개가 introduce된다. \(I_u^+\)는 user u가 관측한 적 있는 item의 set이고, \(U_i^+\)는 item i를 관측한 적 있는 user u의 set이다. 그러면 이 set들을 통해 \(\mathcal D_S\)라는 triple을 다음과 같이 정의할 수 있다.

    + + +

    \[D_S := {(u,i,j) ~|~ i \in I_u^+ \mbox{ and } j \notin I_u^+ }.\]

    + + +

    즉, user u, user u가 관측한 item i와 관측하지 못한 item j 이렇게 셋의 triple인 것이다.

    + + +

    이제 이 논문의 핵심아이디어인 pair-wise ranking에 대해 살펴보자. 이 논문은 먼저 각각의 user u에게 item i와 item j간의 pair-wise ranking이 존재한다고 가정한다. 이 논문에서는 user u가 item i를 j보다 높은 order를 가질 때 \(i >_u j\)의 꼴로 표현한다. 이 pair-wise ranking은 (혹은 order는) totality, antisymmetry, transitivity를 만족하는 order로 정의된다. 자세한 수식은 논문에 있으니 생략한다.

    + + +

    이 논문은 user u가 item i를 item j보다 더 높게 평가할 확률을 다음과 같이 가정하고 있다.

    + + +

    \[Pr( i >_u j | \Theta) = \sigma(\hat r_{uij} (\Theta)).\]

    + + +

    여기에서 \(\hat r_{uij} := \hat r_{ui} - \hat r_{uj}\)로 정의되는 값이고, \(\sigma\)는 sigmoid function으로, \(\sigma(x) = \frac{1}{1+\exp(-x)}\)의 꼴로 정의된다. 각각의 user에 대해 pair-wise ranking에 대한 확률을 정의했고, 또한 실제 관측값도 있기 때문에, 우리는 ranking에 대한 likelihood를 정의할 수 있고, prior를 가정하게 되면 posteriori역시 계산할 수 있다. 먼저 likelihood \(\prod_u p(>_u | \Theta)\)부터 계산해보자. (이전과 마찬가지로 user들은 전부 independent하다고 가정했기 때문에 곱으로 표현된다)

    + + +

    \[\prod_u p(>_u | \Theta) = \prod_{u,i,j} \prod_u p( i >_u j | \Theta)^{I_{(u,i,j) \in \mathcal D_S}} (1 - p( i >_u j | \Theta) )^{I_{(u,i,j) \notin \mathcal D_S}} \]

    + + +

    \(I_x\)는 x가 true면 1이고 false면 0인 indicator function이다. 여기에서 order를 정의할 때 totality와 antisymmetry를 가정하였기 때문에, 위 식을 잘 건개하면 아래와 같은 간단한 식으로 표현하는 것이 가능하다고 한다.

    + + +

    \[\prod_u p(>_u | \Theta) = \prod_{(u,i,j) \in \mathcal D_S} p( i >_u j | \Theta). \]

    + + +

    [3]과 같이 prior를 zero mean normal distribution으로 정의하게 되면, log posteriori는 다음과 같이 표현된다.

    + + +

    \[ \max \ln Pr(\Theta | >_u) = \max \sum_{(u,i,j) \in \mathcal D_S} \ln \sigma(\hat x_{uij}) - \lambda \| \Theta \|^2. \]

    + + +

    이 문제는 stochastic gradient descent로 푸는 것이 가능하다. 자세한 미분 값은 논문에 있으니 생략한다. 참고로 이 논문은 이 문제를 optimization했을 때 얻을 수 있는 solution이 AUC (area under the ROC curve, ROC curve는 wiki 참고)를 maximization하는 것과 비슷한 문제라는 것을 따로 증명해두었으니 관심이 있다면 한 번쯤 읽어보면 좋을 것 같다.

    + + +

    [4]에서 제안한 BPR (baysian personalized ranking) problem을 풀기 위해서 SGD를 사용한다는 언급을 했는데, 문제가 하나 있다. 바로 triple \(\mathcal D_S\)의 개수가 너무 많아서 전부 uniform하게 뽑기에는 데이터가 너무너무 많다는 것이다. 그래서 [5]에서는 adapted sampling 방식을 제안하고 있다.

    + + +

    \[ \max \sum_{(u,i,j) \in \mathcal D_S} w_u w_i w_j \ln \sigma(\hat x_{uij}) - \lambda \| \Theta \|^2. \]

    + + +

    이때 \(w_u = 1/| I_u^+ |\), \(w_i = 1\)로 정의가 된다. 즉, 관측한 아이템이 더 많은 유저는 적게 뽑고, 모든 positivie item은 uniform하게 뽑는다. 마지막으로 \(w_j = \frac{1}{|U||I|} \sum_{u} I_{j \in I_u^+}\)으로 취하게 되는데, 더 많이 사용자들이 관측한, 혹은 좋아한 데이터 위주로 sample을 뽑는 방식이다.

    + + +

    정리해보면, BPR은 다른 논문들처럼 reconstructed error를 바로 measure하는 것이 아니라, pair-wise ranking을 정의하고, 복원된 rating \(\hat r_{ui}\)에 대해 user가 item i보다 j를 좋아할 확률을 sigmoid로 정의한다. 이 확률을 사용해 MAP문제를 정의하는데, 이 문제는 ROC curve의 넓이를 구하는 것과 비슷한 문제가 된다. 이때, sigmoid function을 step function으로 바꾸면 완전히 ROC curve의 넓이를 구하는 것과 같은 문제가 된다. Sigmoid가 step function의 가장 popular한 differentiable approximation 중 하나이기 때문에 sigmoid로 정의하게 되는 것이다. Algorithm은 SGD를 사용하는데, 데이터 셋이 user u가 관측한 item i와 관측하지 않은 item j의 triple이기 때문에 uniform sampling을 하게 되면 결과가 좋지 않을 수 있다. 때문에 adaptive하게 (u,i,j)에서 j 고를 때, popular한 j를 더 고르도록 sample을 하여 성능을 개선하고 있다.

    + + + + +
    정리
    + + +

    이 글에서는 Implicit feedback에 대해 recommendation을 어떻게 할 수 있을지 서로 다른 세 가지 접근방법을 소개했다. 첫 번째는 가장 기본적인 방법으로, confidence level \(c_{ui}\)를 정의하고, real value variable인 \(r_{ui}\)를 binary variable인 \(p_{ui}\)로 바꾼 다음 optimization을 푸는 방법에 대해 소개했다. 두 번째로는 RMSE를 optimization하는 대신, u가 i를 좋아할 확률을 모델링하고, 주어진 데이터에 대해 MAP를 푸는 방법에 대해 소개했다. 마지막으로는 각각의 user별로 item들끼리의 personalized pair-wise ranking을 정의하고, 역시 마찬가지로 u가 i보다 j를 좋아할 확률을 모델링하고 이것의 MAP를 구하는 방법에 대해 소개했다. 알고리즘은 SGD로 해결할 수 있지만, 조금 더 smart하게 item을 뽑는 adaptive sampling을 사용할 경우 성능이 더 올라간다고 한다.

    + + + + +
    변경 이력
    + + +
      +
    • 2016년 3월 6일: 글 등록
    • +
    + + +
    References
    + + +
      +
    1. Recommendation System (Matrix Completion)
    2. +
    3. Hu, Yifan, Yehuda Koren, and Chris Volinsky. "Collaborative filtering for implicit feedback datasets.", 2008
    4. +
    5. Johnson, Christopher C. "Logistic matrix factorization for implicit feedback data.", 2014
    6. +
    7. Gantner, Zeno, et al. "Bayesian personalized ranking for non-uniformly sampled items.", 2012
    8. +
    9. Rendle, Steffen, et al. "BPR: Bayesian personalized ranking from implicit feedback.", 2009
    10. +
    + + + + +
    + + +

    Machine Learning 스터디의 다른 글들

    + + + + +]]>
    +
    + <![CDATA[Machine learning 스터디 (17) Recommendation System (Matrix Completion)]]> @@ -33,7 +300,7 @@
    Matrix Completion
    -

    그러면 이제 추천 문제를 보다 엄밀하게 정의해보도록 하자. 먼저 데이터에 대해 살펴보도록 하자. 이 문제에서는 사용자와 상품이라는 두 가지 요소들이 존재한다. 사용자 \(u\)가 아이템 \(i\)를 얼마나 좋아할 것인지 나타내는 값을 rating \(r_{ui}\)라 하자. 이때, 이 rating은 Netflix처럼 1에서 5 사이의 real value일수도 있으며, 아마존이나 페이스북처럼 클릭했는지 하지 않았는지에 대한 데이터일수도 있다. 앞선 경우는 사용자가 자신이 얼마나 이 아이템을 좋아하는지 '명시적으로' 나타냈기 때문에 explicit feedback이라 부르며, 후자의 경우는 사용자가 해당 상품을 좋아했는지 싫어했는지 표현을 직접적으로 하지 않으므로 'implicit feedback'이라고 부른다. 이에 대해서는 나중에 다른 글을 통해 더 자세히 다루도록 하겠다. 지금은 \(r_{ui}\)의 정확한 값을 알고 있고, 이 값이 전혀 noise가 없는 값이라고 가정하고 문제를 계속 설명하도록 하겠다. 이런 경우 우리가 가지고 있는 데이터는 \(r_{ui}\)들의 값이 될 것이고, 대략 아래와 같은 방식으로 matrix로 표현할 수 있을 것이다.

    +

    그러면 이제 추천 문제를 보다 엄밀하게 정의해보도록 하자. 먼저 데이터에 대해 살펴보도록 하자. 이 문제에서는 사용자와 상품이라는 두 가지 요소들이 존재한다. 사용자 \(u\)가 아이템 \(i\)를 얼마나 좋아할 것인지 나타내는 값을 rating \(r_{ui}\)라 하자. 이때, 이 rating은 Netflix처럼 1에서 5 사이의 real value일수도 있으며, 아마존이나 페이스북처럼 클릭했는지 하지 않았는지에 대한 데이터일수도 있다. 앞선 경우는 사용자가 자신이 얼마나 이 아이템을 좋아하는지 '명시적으로' 나타냈기 때문에 explicit feedback이라 부르며, 후자의 경우는 사용자가 해당 상품을 좋아했는지 싫어했는지 표현을 직접적으로 하지 않으므로 'implicit feedback'이라고 부른다. 이에 대해서는 나중에 다른 글을 통해 더 자세히 다루도록 하겠다. (Implicit feedback에 대한 글을 새로 추가하였다) 지금은 \(r_{ui}\)의 정확한 값을 알고 있고, 이 값이 전혀 noise가 없는 값이라고 가정하고 문제를 계속 설명하도록 하겠다. 이런 경우 우리가 가지고 있는 데이터는 \(r_{ui}\)들의 값이 될 것이고, 대략 아래와 같은 방식으로 matrix로 표현할 수 있을 것이다.

    @@ -137,7 +404,7 @@

    이 pair-wise optimization 문제는 non-convex 문제이지만, gradient descent method로 local optimum에 수렴하는 결과를 얻을 수 있으며 실제로 꽤 효율적으로 좋은 결과를 얻을 수 있다.

    -

    또 다른 solver로는 Alternating Least Square (ALS) 라는 방법이 있다. 이 방법은 alternative하게 주어진 objective를 update하는 방법인데, 주어진 objective가 pairwise optimization으로 생각하면 non-convex이지만, p나 q 중 하나를 고정하고 나머지에 대해 optimization을 하게 되면 convex, 그것도 closed form으로 계산된다는 점을 이용한 방법이다. 따라서 이 방법을 사용해 예전에 설명했었던 k-means style의 알고리즘을 설계할 수 있는데, 이를 ALS라고 부르는 것이다. Gradient descent가 더 빠른 경우도 있지만, ALS를 사용하게 되면 각각의 element들이 다른 element에 independent하기 떄문에 분산처리가 간편하기 때문에 실제로는 ALS 방법도 많이 사용된다고 한다.

    +

    또 다른 solver로는 Alternating Least Square (ALS) 라는 방법이 있다. 이 방법은 alternative하게 주어진 objective를 update하는 방법인데, 주어진 objective가 pairwise optimization으로 생각하면 non-convex이지만, p나 q 중 하나를 고정하고 나머지에 대해 optimization을 하게 되면 convex, 그것도 closed form으로 계산된다는 점을 이용한 방법이다. 따라서 이 방법을 사용해 예전에 설명했었던 k-means style의 알고리즘을 설계할 수 있는데, 이를 ALS라고 부르는 것이다. Gradient descent가 더 빠른 경우도 있지만, ALS를 사용하게 되면 각각의 element들이 다른 element에 independent하기 때문에 분산처리가 간편하기 때문에 실제로는 ALS 방법도 많이 사용된다고 한다.

    지금까지 설명한 방법은 그야말로 가장 기본이 되는 모델이고, 이 모델을 조금 더 확장해보도록 하자. 가장 간단하게 확장할 수 있는 방법은 bias term을 추가하는 것이다. 예를 들어서 어떤 user는 항상 모든 영화 평점을 비교적 '짜게' 주는 user가 있을 수 있고, 반대로 모든 영화에 점수를 후하게 주는 user도 있을 수 있다. 어떤 영화는 개봉 전부터 평단이나 기자들에게서 호평을 받았거나 유명 배우가 나와 기본 점수가 높을 수도 있고, 그 반대도 가능하다. 따라서 이런 현상들을 반영할 수 있는 bias term이 추가가 되는 것은 지극히 자연스럽다고 할 수 있다. user의 bias를 \(b_u\), item의 bias를 \(b_i\)라고 하면 (이 값들은 vector가 아니라 scalar value이다) user u의 item i에 대한 bias \(b_{ui}\)는 \(b_{ui} = \mu + b_u + b_i\)로 표현할 수 있을 것이다. 여기에서 \(\mu\)는 전체 모든 r_{ui}의 평균 값으로, bias라는 개념이 평균에서부터 얼마나 멀어지는 가에 대한 개념이므로 평균 값도 함께 고려하는 것이다. \(\mu\)는 데이터와 함께 주어지는 값이고, bias term들은 p,q처럼 optimization을 통해 찾아야하는 optimization parameter가 된다. 이를 사용하면 reconstructed rating \(\hat r_{ui}\)는 다음과 같이 표현된다.

    @@ -179,7 +446,7 @@
    정리
    -

    이 글에서는 recommendation 문제가 어떤 문제인지 설명하고, 보다 수학적으로 정의된 matrix completion 문제로 recommendation을 설명한다. 그 후 이 문제를 푸는 가장 popular한 방법인 matrix factorization에 대해서 다룬다. 해당 문제가 non-convex 문제이기 떄문에 convex relaxation을 통해 문제를 푸는 방법 (더 늦게 나온 방법이다), non-convex optimization을 바로 푸는 방법 (Netflix prize에서 실제 사용했던 방법) 두 가지를 소개하고 각각에 대해 간략하게 설명한다. 실제 recommendation은 matrix factorization 뿐 아니라 여러 다른 methodology들을 결합해서 문제를 풀게 되지만, 여전히 단일 model로 가장 좋은 performance를 보여주는 것은 matrix factorization을 기반으로 한 방법론들이기 떄문에 matrix factorization을 제대로 아는 것이 recommendation 문제를 풀기 위한 첫 걸음이라 할 수 있을 것이다.

    +

    이 글에서는 recommendation 문제가 어떤 문제인지 설명하고, 보다 수학적으로 정의된 matrix completion 문제로 recommendation을 설명한다. 그 후 이 문제를 푸는 가장 popular한 방법인 matrix factorization에 대해서 다룬다. 해당 문제가 non-convex 문제이기 때문에 convex relaxation을 통해 문제를 푸는 방법 (더 늦게 나온 방법이다), non-convex optimization을 바로 푸는 방법 (Netflix prize에서 실제 사용했던 방법) 두 가지를 소개하고 각각에 대해 간략하게 설명한다. 실제 recommendation은 matrix factorization 뿐 아니라 여러 다른 methodology들을 결합해서 문제를 풀게 되지만, 여전히 단일 model로 가장 좋은 performance를 보여주는 것은 matrix factorization을 기반으로 한 방법론들이기 때문에 matrix factorization을 제대로 아는 것이 recommendation 문제를 풀기 위한 첫 걸음이라 할 수 있을 것이다.

    @@ -195,13 +462,13 @@
    References
    - +
      +
    1. 인터넷 속의 수학 - How Does Netflix Recommend Movies? (1/2)
    2. +
    3. 인터넷 속의 수학 - How Does Netflix Recommend Movies? (2/2)
    4. +
    5. Kennedy, Ryan. "Low-rank matrix completion.", 2013
    6. +
    7. Candès, Emmanuel J., and Benjamin Recht. "Exact matrix completion via convex optimization.", 2009
    8. +
    9. Koren, Yehuda, Robert Bell, and Chris Volinsky. "Matrix factorization techniques for recommender systems.", 2009
    10. +
    @@ -230,10 +497,20 @@
  • EM algorithm
  • Hidden Markov Model
  • Dimensionality Reduction (LDA, PCA)
  • -
  • Recommendation System (Matrix Completion)
  • +
  • Recommendation System (Matrix Completion) + + +
  • Neural Network Introduction
  • Deep Learning 1 – RBM, DNN, CNN
  • -
  • Reinforcement Learning
  • +
  • Reinforcement Learning + +
      +
    • Multi-armed Bandit
    • +
    +
  • ]]> @@ -1209,185 +1486,6 @@

    Machine Learning 스터디의 다른 글들

    - - -]]> -
    - - - <![CDATA[Machine learning 스터디 (16) Dimensionality Reduction (PCA, LDA)]]> - - 2015-06-17T05:21:00+09:00 - http://SanghyukChun.github.io/72 - 들어가며 - - -

    Machine Learning problem을 풀다보면, 종종 high dimensional 데이터를 다뤄야할 일이 생긴다. 그런데 dimension 높은 데이터를 다루다보면 여러 문제가 발생하는데, 높은 dimension으로 인해 생기는 대표적인 문제가 예전 글에서 다뤘던 Curse of dimensionality이다. 또한 많은 algorithm들에서 dimension이 complexity에 영향을 주는 경우가 많으므로 높은 dimension은 알고리즘의 성능에 악영향을 미치는 경우가 많다. 그렇기 때문에 많은 경우 데이터의 dimension이 높다면 다양한 방식의 dimenionality reduction 기술을 적용해 데이터의 차원을 낮추는 작업을 한다. 일반적으로 많이 사용하는 Dimensionality Reduction으로는 LDA와 PCA가 있으며, ICA, CCA 등의 방법도 종종 사용되며, 그 이외에도 RBM, Auto-encoder 등의 Neural network와 관련된 모델들도 존재한다. 이 글에서는 가장 많이 사용되는 방법들인 LDA와 PCA에 대해서만 다룰 것이다.

    - - -
    Recall: Curse of Dimensonality
    - - -

    Curse of Dimensionality는 데이터의 차원이 높아질수록 발생하는 여러 문제들을 통틀어 일컫는 말이다. 이런 문제가 발생하는 이유는 차원이 높아질수록 우리가 일반적으로 사용하는 Euclidean distance가 예상치 못한 방식으로 동작하기 때문이다. 예를 들어 d-차원 공간에서 임의의 점으로부터 거리가 1인 점들을 모아놓은 공간을 생각해봤을 때, d가 점점 커지면 커질수록 그 구의 대부분의 부피가 거의 surface에 가까운 엄청나게 얇은 shell에 존재한다는 것을 이미 예전 글에서 증명한바 있다. 다시 말해서 아주 높은 차원의 데이터는 우리가 원하지 않는 방향으로 움직일 가능성이 크다.

    - - -
    Feature Extraction
    - - -

    많은 상황에서 차원의 크기는 feature의 개수를 의미한다. 예를 들어 키, 몸무게, 나이, 성별이라는 네 가지 정보를 가지고 클러스터링을 한다고 생각해보자. 이 경우 데이터의 차원은 4이다. 그런데 아마도 이 네 가지 정보 이외에도 소득, 학력, 자산크기 등의 정보 등을 추가로 사용해 클러스터링을 한다면 더 좋은 클러스터링이 가능할지도 모른다. 문제는, 모든 feature가 전부 의미있는 feature는 아닐 수 있다는 것이다. 몸무게 정보를 사용하여 클러스터링한 것과 사용하지 않고 클러스터링한 것 중에서 몸무게 정보를 사용하지 않고 클러스터링 한 것이 더 좋을 수도 있다는 것이다. 이렇게 주어진 정보들 중에서 정말 의미 있는 feature를 뽑아내는 과정을 feature extration이라고 한다. 가장 간단한 feature extraction은 모든 feature를 사용해보기도 하고 사용해보지 않기도 하면서 \(2^d\) 개의 조합을 모두 확인해보는 것이다. 그러나 이 방법은 차원의 크기에 exponential할 뿐 아니라, 만약 기존 feature가 highly correlate되어있고, 여러 개의 feature를 묶어서 한 feature로 만들어야 성능이 좋아지는 경우 등에 대해 좋은 성능을 내기 어렵다. 따라서 이런 상황에서도 dimensionality reduction 방법을 사용해 feature를 뽑아낼 수 있다. 만약 우리가 100개의 feature를 가지고 있을 때, '가장 좋은' 30개의 feature만 뽑기 위해서 30차원으로 dimensionality reduction을 하는 것이다.

    - - -
    Dimensionality Reduction
    - - -

    데이터의 차원을 낮춘다는 것의 의미는, 현재 데이터가 존재하는 차원에서 그보다 낮은 다른 차원으로 데이터들을 mapping시키는 map을 찾는다는 것과 같다고 할 수 있다. 이때 어떤 임의의 차원에서 그보다 낮은 임의의 낮은 차원으로 가는 mapping은 셀 수 없이 많다. 그렇다면 우리는 어떤 mapping을 선택해야할까? 예를 들어 가장 간단한 방법으로, d 차원 데이터를 d' 차원으로 보내고 싶을 때, 앞에서 설명했던 간단한 방법처럼 임의의 d' 개의 축을 골라서 그 축만 사용하는 방법도 있을 수 있고, d에서 d'으로 가는 linear map을 임의로 하나 고르는 것도 가능하다. 물론 non linear map도 가능하지만, 이 글에서 다룰 LDA와 PCA 두 가지 방법은 모두 linear mapping을 찾는 알고리즘이다. LDA는 supervised learning이며, PCA는 unsupervised learning에 해당하게 된다. 모든 문제에서 데이터는 matrix \(X\)로 표기되며, 데이터의 차원은 d이고, 개수는 n개이다. 따라서 \(X\)는 d by n matrix가 된다.

    - - -
    LDA
    - - -

    Linear Discriminant Analysis (LDA)는 Dimensionality reduction만을 위한 방법은 아니다. LDA로 약어가 표시되는 것들이 꽤 많아서 (예: Latent Dirichelt Allocation) 이 모델을 처음 제안한 사람의 이름을 따서 Fisher's LDA 라고 부르기도 한다. LDA는 여러 클래스가 존재할 때 그 클래스들을 최대한 잘 분리시키는 projection을 찾는다. 철학은 굉장히 단순한데, projection 시킨 데이터들에서 같은 클래스에 속하는 데이터들의 variance는 최대한 줄이고 (\(\sigma_{within}\)), 각 데이터들의 평균 값들의 variance는 최대한 키워서 (\(\sigma_{between}\)) 클래스들끼리 최대한 멀리 떨어지게 만드는 것이다. 이를 수식으로 표현하면 다음과 같은 수식을 얻을 수 있다.

    - - -

    \[S = \frac{\sigma_{between}^2}{\sigma_{within}^2}\]

    - - -

    일단 가장 간단한 상황인 클래스가 2개일 때의 상황만 고려해보도록하자. 참고로 LDA의 결과는 항상 클래스 개수 - 1 개까지의 벡터 밖에 찾을 수 없기 때문에, 이 상황에서 우리가 찾게 될 projection은 1차원 projection을 찾는 것이므로 간단하게 vector \(w\)로 기술하도록 하겠다. 클래스가 2개 뿐이라면, 위의 식은 엄청 간단한 수식으로 바뀌게 된다. 먼저 \(\sigma_{between}\)은 데이터가 단 두 개 뿐이기 때문에 간단하게 \( (w \cdot \mu_1 - w \cdot \mu_2 )^2 \)으로 표현이 된다. 이때, \(\mu_1, \mu_2\)는 각각 1번째 클래스와 2번째 클래스에 속한 데이터들의 평균 값이다. 다음으로 \(\sigma_{within}\) 역시 어렵지 않게 계산할 수가 있다. Projection을 하게 되면 데이터의 variance는 \(w^\top \Sigma w\)로 표현이 되기 때문에, 1번 클래스와 2번 클래스에 대해 이 값을 계산하고 더해주기만 하면 된다. 식을 정리해보면

    - - -

    \[w = \arg\min_w \frac{\sigma_{between}^2}{\sigma_{within}^2} = \arg\min_w\frac{(w \cdot \mu_1 - w \cdot \mu_2 )^2}{w^\top \Sigma_1 w + w^\top \Sigma_2 w} \]

    - - -

    그리고 위 식을 w에 대해 미분하고 좀 정리해보면 \(w \propto (\Sigma_1 + \Sigma_2)^{-1}(\mu_1 - \mu_2) \) 라는 식을 얻을 수 있다.

    - - -
    Multiclass LDA
    - - -

    - -

    그러면 클래스가 2개보다 많을 때, 벡터 \(w\)가 아닌 subspace \(U\)를 찾는 과정은 어떻게 되는지 살펴보도록하자. 다시 \(\sigma_{within}\)과 \(\sigma_{between}\)을 살펴보자. 먼저 \(\sigma_{between}\)은 위와 비슷한 형태로 표현할 수 없다. 그러나 우리가 각각의 클래스의 평균을 \(\mu_i\)라고 정의한다면, 그냥 이 값들의 variance를 계산하기만 하면 된다. 이 variance는 \(\sum_i (\mu_i - \mu)(\mu_i-\mu)^\top\)로 표현이 된다. 이때 \(\mu\)는 모든 평균들의 평균이다. 이 variance가 있으므로, projection하여 얻는 variance도 앞 뒤에 proejction matrix를 곱해 쉽게 계산할 수 있다. \(\sigma_{within}\)은 위와 비슷한 방식으로 \(\sigma_{within} = U^\top \left(\sum_i \Sigma_i \right) U \)로 표현할 수 있다. 이때 \(\Sigma_i\)는 i번째 클래스의 variance이다. 이 식을 정리해보면 다음과 같은 식을 얻는다.

    - - -

    \[U = \arg\min_U \frac{\sigma_{between}^2}{\sigma_{within}^2} = \arg\min_U \frac{U^\top \left(\sum_i (\mu_i - \mu)(\mu_i-\mu)^\top\right) U}{U^\top \left(\sum_i \Sigma_i \right) U} = \arg\max_U \frac{U^\top A U}{U^\top B} \]

    - - -

    \(A\)와 \(B\)는 각각 괄호 안에 있는 값을 의미한다. 위 식에서 보게 되면, 분자에 해당하는 부분이 rank가 클래스 개수 - 1 이기 때문에, 우리가 이 식을 풀었을 때도 \(U\)의 rank가 클래스 개수 - 1이 되므로 LDA가 구할 수 있는 subspace의 축 개수가 클래스 개수 - 1로 제한되는 것이다. 이 식을 풀기 위해서는 eigenvalue를 계산하는 것으로 간단하게 식을 풀 수 있다. 지금부터 왜 이 식이 eigenvalue를 푸는 것으로 해결이 가능한지를 살펴보자.

    - - -
    General Eigenvector problem
    - - -

    문제를 간단하게 하기 위해 전체 subspace가 아닌 vector \(w\)만 고려해보도록 하자. 따라서 식은

    - - -

    \[\min_w \frac{w^\top A w}{w^\top B w}\]

    - - -

    로 표현 가능하다. 이제 이 식을 w에 대해서 미분해보면 다음과 같은 식을 얻게 된다

    - - -

    \[\left(w^\top B w\right)^2 \big[ 2A w \left( w^\top B w \right) - 2B w \left( w^\top A w \right) \big] = 0\]

    - - -

    이를 잘 정리하면

    - - -

    \[Aw = \frac{w^\top A w} {w^\top B w} B w\]

    - - -

    로 표현이 되고, 우리가 원래 풀려고 했던 \(\frac{w^\top A w} {w^\top B w}\)를 \(\lambda\)라고 정의하면 식이 \(A w = \lambda B w\)라는 엄청 간단한 식이 나오게 된다. 이런 형태를 만족하는 \(\lambda\)를 찾는 문제를 general eigenvalue problem이라 부른다. 만약 \(B\)가 Identity matrix라면 우리가 아는 일반 eigenvalue problem이 된다. 따라서 원래 문제가 \(\lambda\)의 minimum을 찾는 것이었으니 이제 가장 작은 general eigenvalue를 찾기만 하면 우리가 풀고 싶었던 문제를 풀 수 있는 것이다.

    - - -
    PCA
    - - -

    Principal Component Analysis (PCA)는 Dimensionality reduction의 가장 대표적인 방법 중 하나이다. PCA는 projection된 데이터의 variance가 최대화되는 projection matrix를 찾는 문제이다. 그런데 Eckart–Young theorem에 의해서, 이 문제의 답이 데이터 \(X\)에 대한 Singular value decomposition(SVD)으로 계산할 수 있다는 것이 알려져 있다. 그래서 PCA를 variance를 maximize하는 원래 정의대로 설명이 하는 경우도 많고, low rank matrix 문제로 설명하는 경우도 있다. 이 글에서는 low rank matrix estimation 문제로 설명하도록 하겠다. 이 문제의 objective는 아래 식과 같다.

    - - -

    \[\min_{rank(Z)=k} \| X - Z \|_F^2\]

    - - -

    이때 \(\|A\|_F\)는 Frobenius norm을 의미한다. 이 norm은 matrix의 모든 element들의 제곱을 더한 값이며, \(\| A \|_F^2 = {\tt tr} (A A^\top) = {\tt tr} (A^\top A) \) 라는 특성이 알려져있다. 이제 위의 식에서 \(Z\)를 UV decomposition한 후 대입해보면 다음과 같은 식을 얻는다.

    - - -

    \[\min_{U\in R^{d \times k}, V\in R^{n \times k}, U^\top U = I} \|X - UV^\top\|_F^2\]

    - - -

    이 식을 \(V\)에 대해 미분하면 optimal한 V는 \(V=X^\top U\)로 표현이 됨을 알 수 있으며, 이를 위의 식에 대입한 후, Frobenius norm의 성질을 잘 활용하면 아래와 같은 식이 나온다.

    - - -

    \[\max_{U\in R^{d \times k}, U^\top U = I} {\tt tr} (U^\top X X^\top U)\]

    - - -

    가 되며, 이 식의 답은 SVD를 통해 계산할 수 있다는 것을 알 수 있다. 그러나 만약 \(X\)의 mean이 0가 아니게 되면 UV decomposition을 하는 과정에서 문제가 생기게 되서 같은 방식으로 깔끔하게 구할 수가 없다. 이 과정은 다소 복잡하므로 생략하고 결과만 얘기하면, 그냥 \(\hat X = X - \frac{1}{n} X \mathbf 1\)을 SVD하면 된다. 뒤에 있는 term은 그냥 X의 평균값이다.

    - - -

    정리하면, 임의의 데이터 X에 대한 PCA는 다음과 같이 구할 수 있다.

    - - -
      -
    1. 데이터 \(X\)의 empirical mean을 계산한 후 모든 데이터에서 평균을 빼준다.
    2. -
    3. 새로 만들어진 데이터 \(\hat X\)의 가장 큰 singular value부터 k번째 큰 singular value까지에 대응하는 singular vector들을 구한다. (SVD를 통해)
    4. -
    5. 뽑아낸 k개의 singular vector로 U를 구성하고 return한다.
    6. -
    - - -

    쉽지만 그만큼 강력하다. 하지만 PCA의 경우 Frobenius norm의 제곱값을 사용하므로 각 element들이 norm을 계산할 때 한 번 제곱되고, 다시 전체에 제곱을 취할 때 또 제곱이 취해지므로 조금이라도 원래 데이터와 다른 outlier가 존재하게 된다면 그 효과가 굉장히 극적으로 증폭되기 때문에 noise에 취약하다. 이를 방지하기 위해 objective의 norm을 1-norm으로 바꾸거나 하는 등의 robust PCA 연구도 활발하게 진행되고 있다.

    - - -
    정리
    - - -

    Dimensionality Reduction은 매우 유용하고 많이 쓰이는 툴이다. 특히 PCA는 굉장히 빈번하게 사용되고 알고리즘도 매우 간단하기 때문에 알아두면 쓰임새가 많다. 이 글에서는 LDA와 PCA만 다뤘지만, ICA, CCA, RBM 등 굉장히 많은 dimensionality reduction 기술들이 존재한다. 이 중 RBM은 추후에 다시 한 번 자세하게 설명하도록 하겠다.

    - - - - -
    변경 이력
    - - -
      -
    • 2015년 6월 17일: 글 등록
    • -
    - - -
    Reference
    - - -
      -
    • Nie, Feiping, Jianjun Yuan, and Heng Huang. “Optimal mean robust principal component analysis.” Proceedings of the 31st International Conference on Machine Learning (ICML-14). 2014.
    • -
    - - -
    - - -

    Machine Learning 스터디의 다른 글들

    - -
    • Machine Learning이란?
    • Probability Theory
    • diff --git a/blog/categories/machine-learning-study/index.html b/blog/categories/machine-learning-study/index.html index 05ffab68..8d5c1bb9 100644 --- a/blog/categories/machine-learning-study/index.html +++ b/blog/categories/machine-learning-study/index.html @@ -94,6 +94,21 @@

      Category: Machine-Learning-Study

      2016

      + + + +


      diff --git a/blog/categories/machine-learning/atom.xml b/blog/categories/machine-learning/atom.xml index 4a643fcf..e5161729 100644 --- a/blog/categories/machine-learning/atom.xml +++ b/blog/categories/machine-learning/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Machine-Learning | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ @@ -13,6 +13,273 @@ Octopress + + <![CDATA[Machine learning 스터디 (17-1) Recommendation System with Implicit Feedback]]> + + 2016-03-06T01:30:00+09:00 + http://SanghyukChun.github.io/95 + 들어가며 + + +

      이전 글[1]에서 다룬 recommendation system은 사용자가 점수를 정확하게 매긴 경우에 대해, 즉 explicit feedback에 대해서만 문제를 푸는 방식이다. 그러나 실제로는 사용자가 점수를 직접 매기는 대신에 단순히 클릭했거나, 조회, 구매한 간접적인 정보, 즉 implicit feedback에만 의존하게 되는 경우도 빈번하게 발생하게 된다. 이 글에서는 그런 implicit feedback만 존재하는 상황에서 어떻게 matrix completion 문제를 디자인하고 해결하는지에 대해 총 세 개의 논문을 들어 설명할 것이다.

      + + +

      이 글의 맨 앞은 implicit feedback이 정확히 무엇이고, 어떤 상황의 문제들이 있는지에 대해 설명할 것이다. 그리고 총 세 개의 논문에서 어떤 방법으로 문제를 접근하는가 설명하도록 할 것이다. 소개할 논문은 Collaborative Filtering for Implicit Feedback Datasets [2], Logistic Matrix Factorization for Implicit Feedback Data [3], Bayesian Personalized Ranking for Non-Uniformly Sampled Items [4] 총 세가지이다.

      + + + + +
      Explicit Feedback and Implicit Feedback
      + + +

      본격적으로 논문들이 제안한 방법론을 살펴보기 전에, explicit feedback과 implicit feedback의 차이점에 대해 논해보도록 하겠다. 먼저 explicit feedback은 사용자가 정확하게 본인이 얼마나 이 item에 호감이 있는지를 수치로 feedback을 주는 것이다. 예를 들어서 Netflix problem의 별점 데이터는 정확하게 1점부터 5점 사이의 well-define된 범위를 가진다. 그러나 실제로는 사용자의 item에 대한 정확한 호감도를 요구하는 것이 어려울 때가 있다. 아마존의 상품 추천을 예로 들어보자. 아마존이 가질 수 있는 데이터는 사용자가 어떤 상품들을 조회하였는지와, 어떤 상품들을 구매하였는지 정도의 정보 밖에 가질 수 없다. 이 경우 사용자가 살펴본 물건들이 사용자가 정말 매력있게 느껴서 살펴본 것인지, 만약 그렇다면 얼마만큼의 호감도가 있는지 판별하는 것이 매우 어려울 것이다. 이런 종류의 데이터를 사용자가 직접적인 점수를 주는 대신 간접적인 정보만을 제공한다고 하여 implicit feedback이라 부른다. 이외에도 sound cloud나 youTube 등에서 사용자가 재생한 재생목록이나 반복하여 청취 혹은 시청한 횟수 등의 데이터도 마찬가지로 implicit feedback의 대표적인 예가 될 수 있다.

      + + +

      Implicit feedback에서 관측 값은 click이나 재생 횟수 (0 혹은 0보다 큰 정수) 일 수도 있고, 음악 등의 item을 재생한 총 시간 (0 이상의 실수) 일 수도 있다. 한 가지 주의할 점은, explicit feedback처럼 사용자가 구체적으로 item에 대한 preference를 제공하지 않기 떄문에 사용자가 선호하지 않아서 선택하지 않은 item과 아직 관측하지 않은, 그러나 잠재적으로 흥미가 있는 item 모두 값이 0일 것이라는 점이다. 보통 사용자들이 item을 굉장히 조금만 click하거나 (뉴스) 사용하거나 (음악, 동영상) 구매하기 때문에 (쇼핑) 실제로는 matrix의 거의 대부분이 비어있고 아주 일부분의 데이터만 관측되기 때문에, negative observation이 positive observation의 수를 압도한게 되고, 때문에 이런 점을 고려하지 않고 모델을 설계하게 되면 아주 일부분의 positive 데이터와 거의 대부분의 negative observation (값이 0인 observation)들에의해 model이 overfitting된다. 또한, implicit feedback은 굉장히 노이즈가 많기 때문에 주어진 데이터를 얼마나 믿을 수 있을지 알 수 없다는 것이다. 예를 들어서 사용자가 물건을 하나 구매하였더라도, 이 물건에 대해 반드시 긍정적으로 생각할 것이라 기대할 수는 없을 것이다. 가끔은 구매한 물건이 아주 불만족스럽고 다시는 비슷한 물건을 구매하고 싶지 않을 수도 있지 않은가? 이런 두 가지 이슈 (negative observation, confidence)는 recommendation model이 implicit feedback을 처리하기 위해 반드시 고려되어야 할 이슈가 된다.

      + + + + +
      Recall: Matrix Factorization with Explicit Feedback
      + + +

      이전 글 [1]에서 다뤘던 objective function은 다음과 같다.

      + + +

      \[\min_{X,Y} \sum_{u,i \in \kappa} (r_{ui} - x_u^\top y_i)^2 + \lambda ( \| x_u \|_2^2 + \| y_i \|_2^2 ) .\]

      + + +

      이전 글에서는 x,y 대신 p,q noataion을 사용했으나 이 글에서는 전부 x, y notation으로 통일하도록 하겠다. 이 objective에 대해서는 이전 글을 참고하면서 읽으면 좋을 것 같다. 원래는 bias term까지 포함해야하지만, 이 글에서는 편의상 bias term은 생략하도록 하겠다. 이 문제는 SGD (Stochastic Gradient Descent), ALS (Alternating Least Square) 등의 solver를 사용해 풀 수 있으며 이 글에서는 자세한 설명을 생략하도록 하겠다.

      + + + + +
      Collaborative Filtering for Implicit Feedback Datasets [2]
      + + +

      Implicit feedback을 처리하는 가장 기본적인 접근법을 소개해보자. 이 논문은 user u가 item i를 선호하는지 하지 않는지 여부를 가르키는 preference vector \(p_{ui}\)를 정의한다. \(p_{ui}\)의 값은 \(sign(r_{ui})\)으로 정의할 수 있다. sign 함수는 input 값의 'sign'을 return하는 함수로, 즉 input이 negative value면 -1, positive value면 1을 return한다. 따라서 perference의 값은 rating r이 0보다 크다면 1이고, r이 0이라면 p도 0이 되는 것이다.

      + + +

      앞서 설명한 것 처럼, preference vector의 값을 항상 신뢰할 수 있는 것은 아니다. 때문에, 이 논문에서는 confience level \(c_{ui}\)라는 것을 정의하게 된다. 우리가 한 가지 가정할 수 있는 것은 만약 \(r_{ui}\)의 값이 크다면, 예를 들어 한 사용자가 한 항목을 엄청 많이 재구매했다거나 한다면, u는 i를 아주 높은 확률로 prefer한다는 사실을 가정할 수 있다. 따라서 confidence level은 r에 대한 increasing function으로 정의하는 것이 타당하다고 할 수 있다. 이 논문에서는 confidence level \(c_{ui}\)를 다양한 방식으로 정의할 수 있다고 언급하고 있으며, 실제 실험에서는 가장 직관적이고 단순한 increasing function은 linear function을 사용한다. 따라서 이 논문에서는 다음과 같은 confidence를 사용한다.

      + + +

      \[c_{ui} = 1 + \alpha r_{ui}.\]

      + + +

      혹은 \(c_{ui} = 1 + \alpha \log (1 + r_{ui}/ \varepsilon)\) 등의 confidence도 대안으로 제안하기는 하지만, 기본적으로 위에서 설명한 linear confidence를 사용하는 듯하다. 한 가지 짚고 넘어가야할 점은, \(c_{ui}\)는 실제 데이터 \(r_{ui}\)와 hyper-parameter \(\alpha\)에 의해서만 정의되므로 optimize해야 할 parameter가 아니라 한 번 confidence를 정의하기만하면 고정되는 constant라는 점이다. 따라서 confidence의 값을 어떻게 정의하더라도 전체 알고리즘의 로직을 바꾸거나 하지는 않는다.

      + + +

      c를 정의하는 것에는 또 하나의 이점이 있다. Parameter \(\alpha\)가 positive observation과 negative observation의 중요도를 조절하는 역할을 하게 되면서, negative feedback에 대한 중요도를 조절할 수 있는 것이다. 예를 들어 \(\alpha\)의 크기가 작다면, positive와 negative observation의 confidence 차이가 큰 \(\alpha\)를 가질 때 보다 상대적으로 작을 것이라는 것을 기대할 수 있게 된다.

      + + +

      이제 objective를 정의할 차례이다. Explicit feedback에서는 복원한 rating \(\hat r_{ui}\)와 관측한 데이터 \(r_{ui}\)의 RMSE를 바로 계산하였으나, 앞서 말한대로 이 값을 그대로 계산하기에는 confidence의 문제가 있다. 이 논문에서는 앞서 정의한 confidence를 고려하여 objective function은 다음과 같이 정의한다.

      + + +

      \[\min_{X,Y} \sum_{u,i \in \kappa} c_{ui}(p_{ui} - x_u^\top y_i)^2 + \lambda ( \| x_u \|_2^2 + \| y_i \|_2^2 ) .\]

      + + +

      맨 처음 objective와 달라진 점은, rating vector r (0 이상의 real value) 이 preference vector p (0 또는 1) 로 바뀌었다는 점과, 각각의 u,i pair에 대해 confidence \(c_{ui}\)가 곱해진다는 점이다. 이때, \(c_{ui}\)는 optimization parameter와는 상관없이 맨 처음 정해지고 변하지 않는 값이므로, 이렇게 바뀐 objective function을 풀기 위해서 이전 문제와 마찬가지로 살짝 변형된 SGD나 ALS 등을 사용할 수 있다. 논문에서는 조금 더 scalability를 고려한 방법론을 제안하는데, matrix product를 조금 더 효율적으로 하도록 matrix들을 decompose하여 조금 더 order가 낮은 연산을 하는 방법을 사용한다. 자세한 알고리즘은 논문을 참고하면 좋을 것 같다.

      + + +

      정리하자면 이 논문은 rating vector r을 preference vector p로 변환하고, confidence level c를 정의한 후, p와 c를 사용해 RMSE objective function을 optimize하는 work인 것이다. 그리고 앞서 설명했던 두 가지 문제는 confidence level c를 정의하는 방법에 의해 해결할 수 있다.

      + + + + +
      Logistic Matrix Factorization for Implicit Feedback Data [3]
      + + +

      앞선 논문 [2]에서는 RMSE를 minimize하는 objective를 설계하였지만, RMSE가 아닌 다른 형태의 objective를 optimize하는 것도 가능하다. 앞선 논문에서 RMSE를 minimize함으로써 얻을 수 있는 효과는 관측한 preference \(p_{ui}\)와 복원한 preference \(\hat p_{ui}\)가 서로 (RMSE의 관점에서) 최대한 비슷한 값을 가지도록 optimization이 된다는 것이다. 이 논문은 perference의 RMSE를 minimize하는 문제 대신, 관측 값 \(r\)과 optimization parameter \(\Theta = (x, y, b)\) 등의 posteriori를 maximization하는 방식을 취한다. 참고로 이 논문은 Spotify에서 발표한 논문으로, 실제 음악 추천에서 응용하고 있는 듯 하다.

      + + +

      계속 강조하듯, 여기에서 실제 유저가 선호하는 것과 유저의 implicit feedback 결과는 다를 수 있다. 그렇기 때문에 이 논문은 u가 i를 좋아할 확률을 logistic function으로 확률적으로 정의한 후, 관측한 데이터로부터 posteriori를 maximize하는 방향으로 학습을 하게 된다. 그러기위해 이 논문은 \(\ell_{ui}\)이라는 새로운 notation을 introduce하고 이를 사용자 u가 item i를 선호하는 'event'로 정의한다. 어렵게 설명했지만, 그냥 'user u가 음악 i를 좋아한다 좋아하지 않는다'에 대한 0, 1 값이다. 이 논문은 주어진 \(\Theta (x, y, b)\)에 대해 \(\ell\)이 1이 될 확률 \(pr_{ui}\)를 다음과 같이 logistic form으로 정의한다.

      + + +

      \[pr_{ui} := Pr[\ell_{ui} = 1 ~|~ \Theta] = \frac{\exp(x_u^\top y_i + b_u + b_i)}{1 + \exp(x_u^\top y_i + b_u + b_i)}. \]

      + + +

      직관적으로 생각해보았을 때, 앞선 논문 [2]은 r의 값이 0보다 크기만 하면 항상 u가 i를 좋아한다고 생각하지만, 이 논문에서는 그것이 r의 값에 대한 확률로 나타난다는 점을 알 수 있다. Reconstructed rating \(\hat r\)을 \(\hat r_{ui} = x_u^\top y_i\)이라고 했을 때, 위의 함수는 \(\hat r_{ui}\)에 대한 증가함수이므로 (\(\hat r_{ui}\)의 값이 \(-\infty\)가 되면 함수값이 0이고, \(\hat r_{ui}\) 값이 \(\infty\)가 되면 함수값이 1이 된다), 위의 식은 rating 값이 더 크면 호감을 가질 확률이 더 높아지는 형태의 확률 함수가 된다.

      + + +

      따라서 이 모델의 likelihood는 positive observation u,i의 set을 \(\mathcal S\)라 하였을 때 다음과 같이 쓸 수 있다.

      + + +

      \[\mathcal L_{naive} (R ~|~ \Theta) = \prod_{u,i \in \mathcal S} pr_{ui} \prod_{u,i \notin \mathcal S} (1-pr_{ui})\]

      + + +

      그러나 앞선 논문 [2]에서도 언급되었듯, negative feedback은 '싫어한다'와 다른 의미를 가지고 있기 때문에, 이 논문에서도 confidence라는 것을 정의하게 된다. [2]와의 차이점이라면 RMSE 관점이 아니라 앞에서 살펴본 likelihood function의 관점에서 정의를 한다는 점이다. 여기에서 \(c_{ui}\)는 \(\alpha r_{ui}\)로 사용하는데, 만약 hyper-parameter \(\alpha\)의 값이 크면 positive observation에 더 큰 비중을 두고, \(\alpha\)의 값이 작다면 negative observation에 더 큰 비중을 두는 식으로 다음과 같이 정의를 하게 된다.

      + + +

      \[ \mathcal L (R ~|~ \Theta) = \prod_{u,i} Pr[ \ell_{ui} | \Theta]^{\alpha r_{ui}} (1 - Pr[ \ell_{ui} | \Theta]).\]

      + + +

      위의 식에서 negative feedback에 대한 (즉, 만약 관측값 \(r_{ui}\)가 0이라면) likelihood 값은 \(\mathcal L (r_{ui} ~|~ \Theta ) = 1 - pr_{ui}\) 가 되므로 앞에서 계산한 naive한 likelihood function과 일치한다. 그러나 positive feedback에 대한 likelihood는 \(pr_{ui}^{\alpha r_{ui}} (1-pr_{ui}) \)가 되므로, \(\alpha\)의 값을 조절함에 따라 앞에서 계산한 값과 차이가 있다.

      + + +

      개인적인 생각으로는 여기에서 저자가 증명을 잘못한 것이 아닐까 생각된다. 만약 Positive feedback의 likelihood에 weight를 주기위해 exponent c를 추가한다고 했을 때, likelihood 식은 다음과 같이 된다.

      + + +

      \[ \mathcal L = \prod_{u,i \in \mathcal S} ( p_{ui}^{\ell_{ui}} (1-p_{ui})^{1-\ell_{ui}} )^{\alpha r_{ui}} \prod_{u,i \notin \mathcal S} p_{ui}^{\ell_{ui}} (1-p_{ui})^{1-\ell_{ui}}. \]

      + + +

      이때, \(r_{ui} > 0\) 일 때 \(\ell = 1\)이고, 혹은 둘 다 0이라는 특성을 잘 사용하면 이 식은 다음과 같이 표현이 된다.

      + + +

      \[\mathcal L = \prod_{u,i} p_{ui}^{\alpha r_{ui}} (1-p_{ui})^{1-\ell_{ui}}. \]

      + + +

      즉, 만약 저자가 의도한대로 positive feedback의 likelihood에 exponent로 weight를 주고 싶었다면, positive feedback의 likelihood function에서 1-p 부분이 없어야한다는 점이다. 이 부분은 저자가 실수를 했거나 혹은 내가 이해를 잘못했을 가능성이 있다. 혹시 몰라서 논문을 좀 찾아봤는데, 약 한 달 전쯤 나온 논문에서 증명한 결과가 내가 증명한 결과와 같은걸 보면, 저자가 틀린게 맞는 것 같다.

      + + +

      그리고 또 다른 문제는 \(pr_{ui}\)는 언제나 값이 0에서 1사이이기 때문에 \(c_{ui} = \alpha r_{ui}\)의 값이 크면 클수록 \(pr_{ui}\)의 값은 오히려 감소하게 된다는 것이다. 그래서 논문의 설명과는 반대로 \(\alpha\)의 값을 키우는 것이 오히려 positive observation의 weight를 낮추는 것이 아닌가하는 생각이 드는데, 논문을 여러 번 다시 읽어보고 계산을 해봐도 아직 아리송하다. 오히려 이렇게 하고 싶었다면, 최종 objective가 완전히 바뀌기는 하지만, 관측된 데이터 \(\mathcal S\)에 대해서 likelihood를 \( (1 + \alpha_{ui}) pr_{ui}\)와 같은 형태로 정의하는 편이 훨씬 좋지 않을까? 왜 base가 1보다 작은데, weight term을 exponent으로 올렸는지 이해가 되질 않는다. 이 부분은 혹시 나중에 이해가 완전히 되면 내용을 추가하도록 하겠다.

      + + +

      다시 본문으로 돌아오자. Likelihood를 계산했으니, prior만 있다면 posteriori를 계산할 수 있게 된다. 이 논문은 \(x, y\)의 prior를 전부 0 mean Normal distribution으로 정의한다. 이렇게 정의할 경우, 나중에 log MAP 문제를 풀게 될 때, L2 regularization과 같은 형태의 식 \(\frac{\lambda}{2} (\| x \|^2 + \| y \|^2) \) 을 얻을 수 있다. 자세한 증명은 생략한다. Prior를 정했으니 이제 posteriori를 구해서 다음과 같이 log MAP 문제를 정의할 수 있다. (논문에서 증명한 결과로, 내가 증명한 결과와는 차이가 있다)

      + + +

      \[ \log Pr[ \Theta | P ] = \sum_{u,i} c_{ui} \widehat r_{ui} - ( 1 + c_{ui} ) \log ( 1 + \exp \widehat r_{ui} ) - \frac{\lambda}{2} ( \| x_u\|^2 + \| y_i \|^2 ). \]

      + + +

      이 문제 역시 다른 문제들 처럼 한 번에 update하기가 어렵기 떄문에 alternative하게 update를 하게 된다. 정확히는 coordinate gradient method를 사용한다 (maximize문제이므로 ascent가 될 것이다). 여기에서 한 가지 문제가 발생하는데, 앞에 붙어있는 summation term이 모.든. (u,i) pair에 대한 summation이기 때문에 한 번 gradient를 계산하는 비용이 어마어마해진다는 것이다. 정확히는 아이템의 개수 I와 유저의 숫자 U에 대해 linear한 비용이 필요하다. 보통 그 둘의 값은 몇 백만, 몇 천만이 될 정도로 크기 때문에 scalability 이슈가 굉장히 중요해진다. 이 논문은 그런 문제를 해결하기 위하여 (속도가 느리다는 문제) AdaGradient를 사용하라거나, 전체에 대해 summation을 하는 대신, 전체 positive pair (u,i)와 일부 negative pair (u,i)만 사용해서 문제를 해결하라고 언급되어있다.

      + + +

      정리하자면 이 논문은 Matrix Factorization을 RMSE minimization 문제가 아닌 MAP 문제로 해결하려는 시도를 한 논문으로, MAP로 바꾸기 위하여 confidence가 포함된 likelihood function을 정의한다. (개인적으로는 이 likelihood function이 왜 이런 꼴을 하고 있는지 이해하지 못하였다) 알고리즘은 coordinate ascent를 사용하지만, 각각의 gradient 값이 아이템과 유저의 개수에 linear하기 때문에 실제 데이터에서 practical하지 못하다는 문제가 발생한다. 이런 문제를 해결하기 위하여 이 논문은 전체 matrix의 거의 대부분을 차지하는 negative observation을 전체 다 사용하는 대신, 일부만 sample하여 사용하는 방식을 제안하고 있다.

      + + + + +
      Bayesian Personalized Ranking for Non-Uniformly Sampled Items [4]
      + + +

      앞의 두 논문은 같은 문제의 objective function만 RMSE와 MAP로 서로 다르게 잡은 경우이지만, 이 논문은 앞의 방법들과 다소 다른 접근 방식을 취하고 있다. 이 논문은 먼저 선행 연구[5]를 조금 발전 시킨 논문인데, 선행 연구에서는 partially observed pair-wise competition 문제를 푸는 Baysian Personalized Ranking (BPR) optimization과 그것을 푸는 알고리즘을 제안하고, 그것을 MF로 확장하고있다. 그리고 그 다음 논문 [4]에서는 원래 논문이 가지는 단점을 negative observation을 adaptive하게 sample하는 방식으로 개선하고 있다.

      + + +

      먼저 핵심 notation들을 정의해보자. 관측된 (u,i) pair는 \(\mathcal S\)라는 set으로 정의된다. 여기에서 새로운 notation \(I_u^+\)와 \(U_i^+\) 2개가 introduce된다. \(I_u^+\)는 user u가 관측한 적 있는 item의 set이고, \(U_i^+\)는 item i를 관측한 적 있는 user u의 set이다. 그러면 이 set들을 통해 \(\mathcal D_S\)라는 triple을 다음과 같이 정의할 수 있다.

      + + +

      \[D_S := {(u,i,j) ~|~ i \in I_u^+ \mbox{ and } j \notin I_u^+ }.\]

      + + +

      즉, user u, user u가 관측한 item i와 관측하지 못한 item j 이렇게 셋의 triple인 것이다.

      + + +

      이제 이 논문의 핵심아이디어인 pair-wise ranking에 대해 살펴보자. 이 논문은 먼저 각각의 user u에게 item i와 item j간의 pair-wise ranking이 존재한다고 가정한다. 이 논문에서는 user u가 item i를 j보다 높은 order를 가질 때 \(i >_u j\)의 꼴로 표현한다. 이 pair-wise ranking은 (혹은 order는) totality, antisymmetry, transitivity를 만족하는 order로 정의된다. 자세한 수식은 논문에 있으니 생략한다.

      + + +

      이 논문은 user u가 item i를 item j보다 더 높게 평가할 확률을 다음과 같이 가정하고 있다.

      + + +

      \[Pr( i >_u j | \Theta) = \sigma(\hat r_{uij} (\Theta)).\]

      + + +

      여기에서 \(\hat r_{uij} := \hat r_{ui} - \hat r_{uj}\)로 정의되는 값이고, \(\sigma\)는 sigmoid function으로, \(\sigma(x) = \frac{1}{1+\exp(-x)}\)의 꼴로 정의된다. 각각의 user에 대해 pair-wise ranking에 대한 확률을 정의했고, 또한 실제 관측값도 있기 때문에, 우리는 ranking에 대한 likelihood를 정의할 수 있고, prior를 가정하게 되면 posteriori역시 계산할 수 있다. 먼저 likelihood \(\prod_u p(>_u | \Theta)\)부터 계산해보자. (이전과 마찬가지로 user들은 전부 independent하다고 가정했기 때문에 곱으로 표현된다)

      + + +

      \[\prod_u p(>_u | \Theta) = \prod_{u,i,j} \prod_u p( i >_u j | \Theta)^{I_{(u,i,j) \in \mathcal D_S}} (1 - p( i >_u j | \Theta) )^{I_{(u,i,j) \notin \mathcal D_S}} \]

      + + +

      \(I_x\)는 x가 true면 1이고 false면 0인 indicator function이다. 여기에서 order를 정의할 때 totality와 antisymmetry를 가정하였기 때문에, 위 식을 잘 건개하면 아래와 같은 간단한 식으로 표현하는 것이 가능하다고 한다.

      + + +

      \[\prod_u p(>_u | \Theta) = \prod_{(u,i,j) \in \mathcal D_S} p( i >_u j | \Theta). \]

      + + +

      [3]과 같이 prior를 zero mean normal distribution으로 정의하게 되면, log posteriori는 다음과 같이 표현된다.

      + + +

      \[ \max \ln Pr(\Theta | >_u) = \max \sum_{(u,i,j) \in \mathcal D_S} \ln \sigma(\hat x_{uij}) - \lambda \| \Theta \|^2. \]

      + + +

      이 문제는 stochastic gradient descent로 푸는 것이 가능하다. 자세한 미분 값은 논문에 있으니 생략한다. 참고로 이 논문은 이 문제를 optimization했을 때 얻을 수 있는 solution이 AUC (area under the ROC curve, ROC curve는 wiki 참고)를 maximization하는 것과 비슷한 문제라는 것을 따로 증명해두었으니 관심이 있다면 한 번쯤 읽어보면 좋을 것 같다.

      + + +

      [4]에서 제안한 BPR (baysian personalized ranking) problem을 풀기 위해서 SGD를 사용한다는 언급을 했는데, 문제가 하나 있다. 바로 triple \(\mathcal D_S\)의 개수가 너무 많아서 전부 uniform하게 뽑기에는 데이터가 너무너무 많다는 것이다. 그래서 [5]에서는 adapted sampling 방식을 제안하고 있다.

      + + +

      \[ \max \sum_{(u,i,j) \in \mathcal D_S} w_u w_i w_j \ln \sigma(\hat x_{uij}) - \lambda \| \Theta \|^2. \]

      + + +

      이때 \(w_u = 1/| I_u^+ |\), \(w_i = 1\)로 정의가 된다. 즉, 관측한 아이템이 더 많은 유저는 적게 뽑고, 모든 positivie item은 uniform하게 뽑는다. 마지막으로 \(w_j = \frac{1}{|U||I|} \sum_{u} I_{j \in I_u^+}\)으로 취하게 되는데, 더 많이 사용자들이 관측한, 혹은 좋아한 데이터 위주로 sample을 뽑는 방식이다.

      + + +

      정리해보면, BPR은 다른 논문들처럼 reconstructed error를 바로 measure하는 것이 아니라, pair-wise ranking을 정의하고, 복원된 rating \(\hat r_{ui}\)에 대해 user가 item i보다 j를 좋아할 확률을 sigmoid로 정의한다. 이 확률을 사용해 MAP문제를 정의하는데, 이 문제는 ROC curve의 넓이를 구하는 것과 비슷한 문제가 된다. 이때, sigmoid function을 step function으로 바꾸면 완전히 ROC curve의 넓이를 구하는 것과 같은 문제가 된다. Sigmoid가 step function의 가장 popular한 differentiable approximation 중 하나이기 때문에 sigmoid로 정의하게 되는 것이다. Algorithm은 SGD를 사용하는데, 데이터 셋이 user u가 관측한 item i와 관측하지 않은 item j의 triple이기 때문에 uniform sampling을 하게 되면 결과가 좋지 않을 수 있다. 때문에 adaptive하게 (u,i,j)에서 j 고를 때, popular한 j를 더 고르도록 sample을 하여 성능을 개선하고 있다.

      + + + + +
      정리
      + + +

      이 글에서는 Implicit feedback에 대해 recommendation을 어떻게 할 수 있을지 서로 다른 세 가지 접근방법을 소개했다. 첫 번째는 가장 기본적인 방법으로, confidence level \(c_{ui}\)를 정의하고, real value variable인 \(r_{ui}\)를 binary variable인 \(p_{ui}\)로 바꾼 다음 optimization을 푸는 방법에 대해 소개했다. 두 번째로는 RMSE를 optimization하는 대신, u가 i를 좋아할 확률을 모델링하고, 주어진 데이터에 대해 MAP를 푸는 방법에 대해 소개했다. 마지막으로는 각각의 user별로 item들끼리의 personalized pair-wise ranking을 정의하고, 역시 마찬가지로 u가 i보다 j를 좋아할 확률을 모델링하고 이것의 MAP를 구하는 방법에 대해 소개했다. 알고리즘은 SGD로 해결할 수 있지만, 조금 더 smart하게 item을 뽑는 adaptive sampling을 사용할 경우 성능이 더 올라간다고 한다.

      + + + + +
      변경 이력
      + + +
        +
      • 2016년 3월 6일: 글 등록
      • +
      + + +
      References
      + + +
        +
      1. Recommendation System (Matrix Completion)
      2. +
      3. Hu, Yifan, Yehuda Koren, and Chris Volinsky. "Collaborative filtering for implicit feedback datasets.", 2008
      4. +
      5. Johnson, Christopher C. "Logistic matrix factorization for implicit feedback data.", 2014
      6. +
      7. Gantner, Zeno, et al. "Bayesian personalized ranking for non-uniformly sampled items.", 2012
      8. +
      9. Rendle, Steffen, et al. "BPR: Bayesian personalized ranking from implicit feedback.", 2009
      10. +
      + + + + +
      + + +

      Machine Learning 스터디의 다른 글들

      + + + + +]]> + + <![CDATA[Machine learning 스터디 (17) Recommendation System (Matrix Completion)]]> @@ -33,7 +300,7 @@
      Matrix Completion
      -

      그러면 이제 추천 문제를 보다 엄밀하게 정의해보도록 하자. 먼저 데이터에 대해 살펴보도록 하자. 이 문제에서는 사용자와 상품이라는 두 가지 요소들이 존재한다. 사용자 \(u\)가 아이템 \(i\)를 얼마나 좋아할 것인지 나타내는 값을 rating \(r_{ui}\)라 하자. 이때, 이 rating은 Netflix처럼 1에서 5 사이의 real value일수도 있으며, 아마존이나 페이스북처럼 클릭했는지 하지 않았는지에 대한 데이터일수도 있다. 앞선 경우는 사용자가 자신이 얼마나 이 아이템을 좋아하는지 '명시적으로' 나타냈기 때문에 explicit feedback이라 부르며, 후자의 경우는 사용자가 해당 상품을 좋아했는지 싫어했는지 표현을 직접적으로 하지 않으므로 'implicit feedback'이라고 부른다. 이에 대해서는 나중에 다른 글을 통해 더 자세히 다루도록 하겠다. 지금은 \(r_{ui}\)의 정확한 값을 알고 있고, 이 값이 전혀 noise가 없는 값이라고 가정하고 문제를 계속 설명하도록 하겠다. 이런 경우 우리가 가지고 있는 데이터는 \(r_{ui}\)들의 값이 될 것이고, 대략 아래와 같은 방식으로 matrix로 표현할 수 있을 것이다.

      +

      그러면 이제 추천 문제를 보다 엄밀하게 정의해보도록 하자. 먼저 데이터에 대해 살펴보도록 하자. 이 문제에서는 사용자와 상품이라는 두 가지 요소들이 존재한다. 사용자 \(u\)가 아이템 \(i\)를 얼마나 좋아할 것인지 나타내는 값을 rating \(r_{ui}\)라 하자. 이때, 이 rating은 Netflix처럼 1에서 5 사이의 real value일수도 있으며, 아마존이나 페이스북처럼 클릭했는지 하지 않았는지에 대한 데이터일수도 있다. 앞선 경우는 사용자가 자신이 얼마나 이 아이템을 좋아하는지 '명시적으로' 나타냈기 때문에 explicit feedback이라 부르며, 후자의 경우는 사용자가 해당 상품을 좋아했는지 싫어했는지 표현을 직접적으로 하지 않으므로 'implicit feedback'이라고 부른다. 이에 대해서는 나중에 다른 글을 통해 더 자세히 다루도록 하겠다. (Implicit feedback에 대한 글을 새로 추가하였다) 지금은 \(r_{ui}\)의 정확한 값을 알고 있고, 이 값이 전혀 noise가 없는 값이라고 가정하고 문제를 계속 설명하도록 하겠다. 이런 경우 우리가 가지고 있는 데이터는 \(r_{ui}\)들의 값이 될 것이고, 대략 아래와 같은 방식으로 matrix로 표현할 수 있을 것이다.

      @@ -137,7 +404,7 @@

      이 pair-wise optimization 문제는 non-convex 문제이지만, gradient descent method로 local optimum에 수렴하는 결과를 얻을 수 있으며 실제로 꽤 효율적으로 좋은 결과를 얻을 수 있다.

      -

      또 다른 solver로는 Alternating Least Square (ALS) 라는 방법이 있다. 이 방법은 alternative하게 주어진 objective를 update하는 방법인데, 주어진 objective가 pairwise optimization으로 생각하면 non-convex이지만, p나 q 중 하나를 고정하고 나머지에 대해 optimization을 하게 되면 convex, 그것도 closed form으로 계산된다는 점을 이용한 방법이다. 따라서 이 방법을 사용해 예전에 설명했었던 k-means style의 알고리즘을 설계할 수 있는데, 이를 ALS라고 부르는 것이다. Gradient descent가 더 빠른 경우도 있지만, ALS를 사용하게 되면 각각의 element들이 다른 element에 independent하기 떄문에 분산처리가 간편하기 때문에 실제로는 ALS 방법도 많이 사용된다고 한다.

      +

      또 다른 solver로는 Alternating Least Square (ALS) 라는 방법이 있다. 이 방법은 alternative하게 주어진 objective를 update하는 방법인데, 주어진 objective가 pairwise optimization으로 생각하면 non-convex이지만, p나 q 중 하나를 고정하고 나머지에 대해 optimization을 하게 되면 convex, 그것도 closed form으로 계산된다는 점을 이용한 방법이다. 따라서 이 방법을 사용해 예전에 설명했었던 k-means style의 알고리즘을 설계할 수 있는데, 이를 ALS라고 부르는 것이다. Gradient descent가 더 빠른 경우도 있지만, ALS를 사용하게 되면 각각의 element들이 다른 element에 independent하기 때문에 분산처리가 간편하기 때문에 실제로는 ALS 방법도 많이 사용된다고 한다.

      지금까지 설명한 방법은 그야말로 가장 기본이 되는 모델이고, 이 모델을 조금 더 확장해보도록 하자. 가장 간단하게 확장할 수 있는 방법은 bias term을 추가하는 것이다. 예를 들어서 어떤 user는 항상 모든 영화 평점을 비교적 '짜게' 주는 user가 있을 수 있고, 반대로 모든 영화에 점수를 후하게 주는 user도 있을 수 있다. 어떤 영화는 개봉 전부터 평단이나 기자들에게서 호평을 받았거나 유명 배우가 나와 기본 점수가 높을 수도 있고, 그 반대도 가능하다. 따라서 이런 현상들을 반영할 수 있는 bias term이 추가가 되는 것은 지극히 자연스럽다고 할 수 있다. user의 bias를 \(b_u\), item의 bias를 \(b_i\)라고 하면 (이 값들은 vector가 아니라 scalar value이다) user u의 item i에 대한 bias \(b_{ui}\)는 \(b_{ui} = \mu + b_u + b_i\)로 표현할 수 있을 것이다. 여기에서 \(\mu\)는 전체 모든 r_{ui}의 평균 값으로, bias라는 개념이 평균에서부터 얼마나 멀어지는 가에 대한 개념이므로 평균 값도 함께 고려하는 것이다. \(\mu\)는 데이터와 함께 주어지는 값이고, bias term들은 p,q처럼 optimization을 통해 찾아야하는 optimization parameter가 된다. 이를 사용하면 reconstructed rating \(\hat r_{ui}\)는 다음과 같이 표현된다.

      @@ -179,7 +446,7 @@
      정리
      -

      이 글에서는 recommendation 문제가 어떤 문제인지 설명하고, 보다 수학적으로 정의된 matrix completion 문제로 recommendation을 설명한다. 그 후 이 문제를 푸는 가장 popular한 방법인 matrix factorization에 대해서 다룬다. 해당 문제가 non-convex 문제이기 떄문에 convex relaxation을 통해 문제를 푸는 방법 (더 늦게 나온 방법이다), non-convex optimization을 바로 푸는 방법 (Netflix prize에서 실제 사용했던 방법) 두 가지를 소개하고 각각에 대해 간략하게 설명한다. 실제 recommendation은 matrix factorization 뿐 아니라 여러 다른 methodology들을 결합해서 문제를 풀게 되지만, 여전히 단일 model로 가장 좋은 performance를 보여주는 것은 matrix factorization을 기반으로 한 방법론들이기 떄문에 matrix factorization을 제대로 아는 것이 recommendation 문제를 풀기 위한 첫 걸음이라 할 수 있을 것이다.

      +

      이 글에서는 recommendation 문제가 어떤 문제인지 설명하고, 보다 수학적으로 정의된 matrix completion 문제로 recommendation을 설명한다. 그 후 이 문제를 푸는 가장 popular한 방법인 matrix factorization에 대해서 다룬다. 해당 문제가 non-convex 문제이기 때문에 convex relaxation을 통해 문제를 푸는 방법 (더 늦게 나온 방법이다), non-convex optimization을 바로 푸는 방법 (Netflix prize에서 실제 사용했던 방법) 두 가지를 소개하고 각각에 대해 간략하게 설명한다. 실제 recommendation은 matrix factorization 뿐 아니라 여러 다른 methodology들을 결합해서 문제를 풀게 되지만, 여전히 단일 model로 가장 좋은 performance를 보여주는 것은 matrix factorization을 기반으로 한 방법론들이기 때문에 matrix factorization을 제대로 아는 것이 recommendation 문제를 풀기 위한 첫 걸음이라 할 수 있을 것이다.

      @@ -195,13 +462,13 @@
      References
      - +
        +
      1. 인터넷 속의 수학 - How Does Netflix Recommend Movies? (1/2)
      2. +
      3. 인터넷 속의 수학 - How Does Netflix Recommend Movies? (2/2)
      4. +
      5. Kennedy, Ryan. "Low-rank matrix completion.", 2013
      6. +
      7. Candès, Emmanuel J., and Benjamin Recht. "Exact matrix completion via convex optimization.", 2009
      8. +
      9. Koren, Yehuda, Robert Bell, and Chris Volinsky. "Matrix factorization techniques for recommender systems.", 2009
      10. +
      @@ -230,10 +497,20 @@
    • EM algorithm
    • Hidden Markov Model
    • Dimensionality Reduction (LDA, PCA)
    • -
    • Recommendation System (Matrix Completion)
    • +
    • Recommendation System (Matrix Completion) + + +
    • Neural Network Introduction
    • Deep Learning 1 – RBM, DNN, CNN
    • -
    • Reinforcement Learning
    • +
    • Reinforcement Learning + +
        +
      • Multi-armed Bandit
      • +
      +
    ]]>
    @@ -1036,67 +1313,6 @@

    이 글은 내가 거의 1년 3개월 전에 들었던 세미나를 바탕으로 쓰여진 글이다. 박사님이 해주신 얘기도 많이 섞여있고 내 개인적인 의견도 많이 섞여있지만, 맨 처음 deep learning을 접할 때 큰 도움이 되었던 세미나인 만큼, 공유할 수 있으면 좋을 것 같아 정리해서 올려본다.

    -]]> -
    - - - <![CDATA[Kaggle competition - Poker rule induction]]> - - 2015-09-26T15:10:00+09:00 - http://SanghyukChun.github.io/87 - 이 글은 2015년 봄 내가 조교를 했었던 KAIST 빅데이터 분석개론 수업에서 중간 프로젝트로 나왔었던 Kaggle Competition을 내가 개인적으로 진행해보면서 느꼈던 점들을 시간 순서에 맞춰 적은 일종의 개발기이다. 깃허브 레포지토리도 있으니 코드가 궁금하다면 이 레포지토리를 확인하면 될 것 같다. 깃허브 기준으로 이 프로젝트는 2015년 3월 20일부터 4월 19일까지 대략 한 달간 진행하였다.

    - - -

    이 competition을 위하여 4가지 정도의 아이디어를 냈었다. 먼저 normal KNN을 최대한 튜닝해보는 것, 문제가 'rule'이라는 것에 초점을 맞추어 rule을 정의하고 그것을 learning하는 방법, 세 번째로 card set에 대한 확률모델을 정의하여 likelihood를 maximization하는 방법, 마지막으로 기존 KNN의 input을 적당하게 바꾸어 KNN으로 처리하는 방법이었다. 이 중 실제로 submission까지 이어진 아이디어는 처음과 마지막 아이디어인데, 첫 번째는 0.67575, 두 번째에는 0.96908을 달성하였다. 이 글에서는 각각의 아이디어를 생각하게 된 계기와 왜 실패했고, 왜 성공했는지에 대해 정리해보도록하겠다.

    - - -

    이 competition을 시작하게 된 계기는, 내가 조교를 맡고 있던 수업에서 프로젝트로 Kaggle competition의 poker rule induction 문제를 풀기로 했기 때문이었다. (competition 링크). 문제 자체가 간단하고, 마음만 먹으면 99% 이상을 찍는 단순한 알고리즘을 만드는 것이 어렵지 않은 문제였기 때문에, 나름의 조건으로 poker rule이 아니라 그 어떤 카드 게임에도 general하게 learning 결과를 적용할 수 있는 framework을 만들자는 것과, 또 하나는 deep learning을 사용하지 않고, 학생들이 이미 알고있는 기초적인 framework에 재미있을 법한 아이디어들을 섞어보기로 하였다. 그렇기 때문에 시작은 가장 간단하고 직관적인 KNN부터 적용해보는 것으로 시작해보았다. training set을 8:2로 나눠서 validation 실험을 진행해본 결과, 그냥 raw data를 사용하고 그냥 KNN을 사용하게 되면, 결과가 50% 정도 밖에 나오지 않았다. 여기에서 내가 해볼 수 있는 가장 간단한 개선은 k의 개수를 조절하는 것과 metric을 바꾸는 것, 그리고 input data를 바꾸는 것이다.

    - - -
    첫 번째 아이디어 KNN
    - - -

    K를 이리저리 조절해보아도 크게 차이가 나지는 않았다. 잘 선택하니 55% 언저리도 나오기는 했지만, K와는 다른 문제가 있어보였다. 그 문제를 해결하기 위해 먼저 input data를 5차원 데이터로 바꿔보았다. 즉, 지금은 5개의 카드 각각에 대해 rank와 suit를 따로 표현하여 10차원 데이터로 표현이 되지만, rank와 suit를 합쳐보는 것이다. 그리고 knn을 해보니 결과는 50% 미만. 왜 이럴까 생각을 해보니 당연히 1~52까지 카드가 배치가 될텐데, 이 숫자는 인덱스를 의미하지 진짜 '거리'를 의미하지 않기 때문에 문제가 발생한다는 것을 알 수 있었다. 따라서 input data는 real number여서는 안되고, 52차원짜리 binary로 개선해야겠다는 생각을 하게 되었다. 즉, 원래 10차원 real value data가 이제 52차원 binary data로 바뀌게 되었다. 같은 카드가 두 번 나오지 않기 때문에 반드시 binary임이 보장된다. 그러나 52차원은 너무 크기 때문에 PCA를 사용하여 차원을 조금 더 낮은 차원으로 보내보기도 하였다. 정리하자면 인풋을 binary로 정의하고, distance는 cosine으로 바꿔보고 PCA에 사용할 low dimension을 잘 선택하고 K를 잘 조절해본 결과 8:2 세팅에서 68.233%까지 성능이 향상되는 것을 볼 수 있었다. 2015년 3월 20일 새벽 2시 반, 리소스 문제로 인해 아직 test data를 서버에 제출하지는 못하였다. 아무래도 코드를 개선해야할 것 같다.

    - - -
    두 번째 아이디어 'rule' learning
    - - -

    그러나 이런 식의 방법을 사용해 KNN의 성능을 개선시킬 수는 있지만, 이는 근본적이 해결책이 되지 못한다. 문제를 해결하기 위해서는 조금 더 문제를 잘 정의하고, 좋은 알고리즘을 찾아야만 한다. 즉, 문제를 어떻게 모델링하느냐에 대한 이슈가 아직 해결되지 않은 것이다. 우리는 무엇을 찾아야하는가? 나는 여기에서 한 가지 생각을 했다. 결국 우리가 찾고 싶은 것은 Poker rule이다. 즉, 만약 우리가 rule에 대한 전체 domain을 정의할 수 있고, 각각의 rule에 대한 performance measure를 정의할 수 있다면, 가장 좋은 rule을 찾는 알고리즘을 디자인 할 수 있지 않을까? 생각이 여기까지 진행되니 바로 자연스럽게 다음 질문이 나오게 되었다. 'rule'은 어떻게 정의해야할까? 처음에는 이렇게 생각했다. 결국 카드게임은 카드들을 비교하여 좋은 '조합'을 가진 사람이 이기는 게임이다. 따라서 나는 5장의 카드들로 이루어진 모든 pair 조합만큼의 dimension을 가지고 (즉, \(5 \choose 2\) = 10개) 두 카드를 비교하는 rule의 개수가 \(n\)개라고 했을 때 각각의 piar들에게 n개 중의 하나에 대응시키게 되면, 우리는 10차원 real value vector로 rule을 표시할 수 있지 않을까? 그런데 문제가 있었다. 앞에서 본 것 처럼, rule의 개수를 \(n\)개 라고 한다고 해서, 1번째 rule과 100번째 rule이 어떤 우열관계가 있는게 아니다. 따라서 결국 binary로 만들어야할 것 같다. 그래서 어떻게 두 카드 사이의 rule을 binary로 만들 수 있을지 가만 생각해보니, 결국 rule이라는 것은 현재 이 카드가 다른 카드 어떤 것과 대응되는지 되지 않는지가 아닌가라는 생각이 들었다. 예를 들어 우리가 52개의 카드를 가지고 있을 때, 같은 숫자를 가진다는 rule은, 총 52 * 52개의 모든 카드 조합 중에서 정확하게 4*13 = 52개의 조합들을 의미하는 것이 아닐까. 다시 말해서, (1, 1), (1, 14), (1, 27), (1,40), (2,2), ... 이런 식으로 정의가 된다고 생각할 수 있는 것이다. 그렇게 생각하게 되면 총 52*52 = 2704개의 rule이 필요하게 되더라.

    - - -

    이렇게 문제를 단순화시키고나니 문제의 목적은 'rule'을 찾는 것이며, rule은 다음과 같은 binary operation으로 정의할 수 있었다. 먼저 우리는 임의의 두 개의 카드 pair에 대해 총 52 * 52 = 2704개의 rule을 가진다. 왜냐하면 1부터 52까지 범위를 가지는 숫자 두 개를 연결하는 방법이 52*52개 만큼 있기 때문이다. 그리고 그 중 k개의 rule을 뽑아내고, k-1개의 binary operation을 사용해 각 점수에 대한 rule을 구해낸다. 예를 들어 카드의 값을 1부터 52까지 대응시켰을 때, 1 pair rule을 이 operation으로 나타내보면, (A 카드의 값이 1이고, B 카드의 값이 1인 rule) or (A 카드의 값이 2이고, B 카드의 값이 2인 rule) or .... 이 될 것이다. 이 경우 k는 13이고, k-1개의 operation들은 모두 or이다. 마찬가지로 2에 대해서 rule을 구하고, 계속해서 9까지 rule을 구한다.

    - - -

    우리가 모든 card set을 가지고 있다면 위의 문제를 direct하게 풀면 문제를 완벽하게 풀 수 있지만, 그렇지 않기 때문에 overfitting이 일어나게 된다. 따라서 나는 이 문제를 어떻게 더 generalize시킬 수 있느냐를 고민해보았다. 이 문제는 Rule의 총 개수를 2704개 보다 훨씬 더 적은 양으로 mapping할 수 있다면 비교적 쉽게 풀 수 있다. 나머지는 전부 training data에서 optimization으로 해결할 수 있는 문제들이니까.

    - - -

    그러나 이윽고 나는 다른 문제에 부딪히게 되었다. Rule이 이것보다 훨씬 많다는 것을 깨달았기 때문이다. Rule은 and operation으로만 이어지는 것이 아니라 or operation으로도 이어질 뿐 아니라 연산 순서 역시 중요했었다. 예를 들어 Rule = (Rule 1 and Rule 2 and Rule 3) or Rule 4 or (Rule 5 and Rule 6) 같은 지저분한 rule도 있을 수 있었기 때문이다. 즉, 내가 문제를 너무 단순하게 봤었다. 이렇게 문제를 생각하게 되면 rule에 대한 강력한 assumption이 없는 이상 더 이상의 generalization은 불가능하고, 직접적으로 rule을 learning하는 것이 어렵다는 결론에 도달하였다.

    - - -
    세 번째 아이디어 grapical probability model
    - - -

    다음으로 내가 생각했던 것은, 확률 모델로 문제를 디자인해보자는 것이었다. 주어진 데이터를 \(X\)라고 한다면 \(p(y|X)\)가 제일 큰 \(y\)를 고르면 되도록 말이다. 여러가지 확률 모델들이 있지만 (예를 들어 naive baysian도 확률모델이다) 내가 생각했던 아이디어는 각각의 \(y\)마다 확률 모델을 만들고, \(X\)가 \(y\)인지 아닌지 binary로 판단하게 하는 방식이었다. 이렇게 생각한 이유는, 실제로 rule이 중복해서 나올 수 있기 때문이다. 예를 들어 Triple은 two pair이기도 하고, four card는 triple이면서 two pair이기도 하다. 이렇게 모델을 정의하고 모든 \(y\)에 대해 지금 \(X\)를 대입한 후, 특정 threshold를 넘는 \(y\) 중에서 가장 큰 숫자를 고르게 하는 것이다.

    - - -

    이 아이디어를 실행하기 위해 가장 중요한 것은 어떤 probability model을 선택하느냐는 것이었다. 내가 처음 생각한 아이디어는 RBM이었지만, RBM은 주어진 데이터에 대한 joint probability만 표현할 수 있지, 어떤 데이터가 그것에 속하지 않는지 learning하는 것이 어려웠다. 이 문제가 근본적으로 기존 방법들로 접근하기 어려운 이유는 sample bias가 너무 심하기 때문이다. 즉, 0점 짜리가 거의 대부분에 속하고 그 다음으로 1점, 2점... 순서로 가기 때문에 각 sample들이 uniform하게 분포하지 않고, 그 때문에 일반적인 방법으로 접근하게 되었을 때 과도하게 0에 치중된 모델이 나오게된다는 점이었다. 나는 이 때문에 각각의 점수별로 모델을 따로 가져가고, 대신 binary로 모델을 풀고 bias를 최대한 해결할 수 있는 방법을 생각해보려 했었다.

    - - -

    그러나 conditional probabilty를 이런 방식으로 leanring할 수 있는 모델이 (학생들이 알 수 있거나 생각할 수 있을법한) 모델 중에서는 도저히 떠오르지 않아서 이 방법도 선택하지 않게 되었다.

    - - -
    마지막 아이디어 결국 다시 KNN
    - - -

    그래서 결국 다시 KNN으로 돌아가게 되었다. 학생 중 하나가 나에게 sorting을 하는 방법에 대해서 물어봤었고, suit를 차라리 없애는 것이 훨씬 결과가 잘 나온다는 얘기를 들었기에 그렇게 한 번 진행해보았다. 그런데 결과가 너무 잘 나오는게 아닌가 (0.96908) 심지어 1-NN을 선택했고, 내가 한 것이라고는 suit를 무시하고 rank만 5개 고르고 5개를 sorting한 것을 넣은 것에 불과했는데. 도저히 이해가 안되서 하루쯤 생각해보니 이게 왜 잘 동작하는지 알 수 있었다. KNN 세팅에서 binary로 바꾸고 하는 과정을 거치지 않았을 때 50%의 성공을 거뒀던 것에 비해 너무 말도 안되는 성능 향상이었기 때문이다. 게다가 1-NN이 아니라 2-NN이나 3-NN의 퍼포먼스가 형편없다는 것도 이해할 수 없었다.

    - - -

    실제 poker rule에서 suit와 카드 순서에 영향을 받는 룰은 Flush 와 straight, 그리고 Royal flush 뿐이지만, 이 녀석들은 거의 나타나지 않는 녀석들이었다. 이렇게 input 데이터를 sorting하게 되면 전체 경우수는 오직 ( 13 choose 5 - 13 ) = 1274 개 뿐이다. 13을 뺸 이유는 같은 rank가 같은 경우는 오직 4개 뿐이기 때문이다. 그런데 training 데이터의 개수는 25,000개 넘기 때문에 엄청 높은 확률로 rank만 고려했을 때 training 데이터와 정확하게 같은 training 데이터가 존재하게 되는 것이다. 이 말은 다시 말하면 왜 K=1 일때만 제대로 동작하는지를 증명하는 말이기도 하다. 즉, 아주 높은 확률로 항상 '정확하게 같은' 데이터 셋을 training데이터에서 고를 수 있으니 당연히 K=1을 선택해야만 제대로 결과가 동작하게 되는 것이다.

    - - -

    나는 여기까지만 시도하고 그만두었다. 이때부터 엄청 바빠지기도 했고, 내가 처음 걸었던 조건처럼 poker에 대한 가정을 최소화한 상태로 학생들도 알 수 있는 간단한 아이디어로 이 문제를 해결하는 것이 어렵다고 느꼈었기 때문이다. 그래도 최종 결과를 96% 정도 달성하였으니 이 정도면 나름 나쁘지 않은 결과가 아닐까 생각한다.

    - ]]>
    diff --git a/blog/categories/machine-learning/index.html b/blog/categories/machine-learning/index.html index 13f9a743..292f8ba1 100644 --- a/blog/categories/machine-learning/index.html +++ b/blog/categories/machine-learning/index.html @@ -94,6 +94,21 @@

    Category: Machine-Learning

    2016

    + + + +


    diff --git a/blog/categories/math-in-internet/atom.xml b/blog/categories/math-in-internet/atom.xml index 1379d630..0af899df 100644 --- a/blog/categories/math-in-internet/atom.xml +++ b/blog/categories/math-in-internet/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Math-in-Internet | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/mobile/atom.xml b/blog/categories/mobile/atom.xml index d7576932..33ed0e0c 100644 --- a/blog/categories/mobile/atom.xml +++ b/blog/categories/mobile/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Mobile | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/modeling/atom.xml b/blog/categories/modeling/atom.xml index d768c415..e95c2918 100644 --- a/blog/categories/modeling/atom.xml +++ b/blog/categories/modeling/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Modeling | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/movie/atom.xml b/blog/categories/movie/atom.xml index 1acf6466..2a70862e 100644 --- a/blog/categories/movie/atom.xml +++ b/blog/categories/movie/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Movie | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/music/atom.xml b/blog/categories/music/atom.xml index f06173eb..0aaabea6 100644 --- a/blog/categories/music/atom.xml +++ b/blog/categories/music/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Music | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/network-science/atom.xml b/blog/categories/network-science/atom.xml index 4f22ba1f..d3c4755f 100644 --- a/blog/categories/network-science/atom.xml +++ b/blog/categories/network-science/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Network-Science | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/network/atom.xml b/blog/categories/network/atom.xml index f38c9464..c57ef323 100644 --- a/blog/categories/network/atom.xml +++ b/blog/categories/network/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Network | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/neural-network/atom.xml b/blog/categories/neural-network/atom.xml index 5cf320e6..0da74da5 100644 --- a/blog/categories/neural-network/atom.xml +++ b/blog/categories/neural-network/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Neural-Network | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/octopress/atom.xml b/blog/categories/octopress/atom.xml index 53e9e921..2b03a1ab 100644 --- a/blog/categories/octopress/atom.xml +++ b/blog/categories/octopress/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Octopress | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/paper-review/atom.xml b/blog/categories/paper-review/atom.xml index 473af58f..39a73fc6 100644 --- a/blog/categories/paper-review/atom.xml +++ b/blog/categories/paper-review/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Paper-Review | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/platform/atom.xml b/blog/categories/platform/atom.xml index 77e58da2..63d2b17f 100644 --- a/blog/categories/platform/atom.xml +++ b/blog/categories/platform/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Platform | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/reinforcement-learning/atom.xml b/blog/categories/reinforcement-learning/atom.xml index 793a0792..4ff1aa09 100644 --- a/blog/categories/reinforcement-learning/atom.xml +++ b/blog/categories/reinforcement-learning/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Reinforcement-Learning | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/research/atom.xml b/blog/categories/research/atom.xml index 4ad9f9ef..9211e877 100644 --- a/blog/categories/research/atom.xml +++ b/blog/categories/research/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Research | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/categories/seminar/atom.xml b/blog/categories/seminar/atom.xml index 69ab0157..700dc1a4 100644 --- a/blog/categories/seminar/atom.xml +++ b/blog/categories/seminar/atom.xml @@ -4,7 +4,7 @@ <![CDATA[Category: Seminar | README]]> - 2016-03-01T17:58:00+09:00 + 2016-03-06T01:30:26+09:00 http://SanghyukChun.github.io/ diff --git a/blog/page/2/index.html b/blog/page/2/index.html index 4f002dc8..bb340aae 100644 --- a/blog/page/2/index.html +++ b/blog/page/2/index.html @@ -9,9 +9,9 @@ - + @@ -87,6 +87,19 @@

    README   + + - -

    subscribe via RSS

    diff --git a/blog/page/3/index.html b/blog/page/3/index.html index 3a28a807..fb879caf 100644 --- a/blog/page/3/index.html +++ b/blog/page/3/index.html @@ -9,10 +9,11 @@ - +Gitlab …"> @@ -88,6 +89,19 @@

    README   + +
    +


    Gitlab을 깔자
    Gitlab Omnibus package for Ubuntu 14.04 를 설치하면서 생겼던 문제들을 trouble shooting하는 과정들

    @@ -201,19 +215,6 @@

    README   -

    - -

    subscribe via RSS

    diff --git a/blog/page/4/index.html b/blog/page/4/index.html index 320eb2a9..61c6f14b 100644 --- a/blog/page/4/index.html +++ b/blog/page/4/index.html @@ -9,10 +9,9 @@ - + @@ -88,6 +87,19 @@

    README   + + - -

    subscribe via RSS

    diff --git a/blog/page/5/index.html b/blog/page/5/index.html index 255422ef..d07a0559 100644 --- a/blog/page/5/index.html +++ b/blog/page/5/index.html @@ -9,8 +9,8 @@ - @@ -87,6 +87,19 @@

    README   + + - -

    subscribe via RSS

    diff --git a/blog/page/6/index.html b/blog/page/6/index.html index e15a9aa5..8873335e 100644 --- a/blog/page/6/index.html +++ b/blog/page/6/index.html @@ -9,9 +9,9 @@ - + @@ -87,6 +87,19 @@

    README   + + - -
    - -


    -좋은 논문 리뷰를 쓰자
    -NDSL에서 있었던 Paper review session에서 나왔던 교수님의 Comment를 정리하고 논문 리뷰는 어떻게 적는 것인가를 다룹니다

    - -Research - - - -

    subscribe via RSS

    diff --git a/blog/page/7/index.html b/blog/page/7/index.html index 62109e38..0c2e9486 100644 --- a/blog/page/7/index.html +++ b/blog/page/7/index.html @@ -9,11 +9,10 @@ - + @@ -89,6 +88,19 @@

    README  
    +


    +좋은 논문 리뷰를 쓰자
    +NDSL에서 있었던 Paper review session에서 나왔던 교수님의 Comment를 정리하고 논문 리뷰는 어떻게 적는 것인가를 다룹니다

    + +Research + + + + +
    + + - -

    subscribe via RSS

    diff --git a/blog/page/8/index.html b/blog/page/8/index.html index d7b6c399..b0ca2ecc 100644 --- a/blog/page/8/index.html +++ b/blog/page/8/index.html @@ -9,11 +9,12 @@ - +Andrew Ng 교수의 …"> @@ -89,6 +90,19 @@

    README   + + - -

    subscribe via RSS

    diff --git a/blog/page/9/index.html b/blog/page/9/index.html index 087fcaf5..b61307eb 100644 --- a/blog/page/9/index.html +++ b/blog/page/9/index.html @@ -9,9 +9,11 @@ - +Octopress로 …"> @@ -87,6 +89,19 @@

    README   + +
    +


    Octopress와 함께하는 첫 번째 post
    Octopress로 진행하는 첫 번째 포스팅

    diff --git a/index.html b/index.html index c3912cb7..bf7440b1 100644 --- a/index.html +++ b/index.html @@ -9,9 +9,9 @@ - + @@ -87,6 +87,19 @@

    README   + + - -

    subscribe via RSS

    diff --git a/sitemap.xml b/sitemap.xml index d837877a..3a716ca2 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -322,15 +322,19 @@ http://SanghyukChun.github.io/73/ - 2016-03-01T17:57:28+09:00 + 2016-03-06T01:29:41+09:00 + + + http://SanghyukChun.github.io/95/ + 2016-03-06T01:29:54+09:00 http://SanghyukChun.github.io/archives/ - 2016-03-01T17:57:28+09:00 + 2016-03-06T01:29:54+09:00 http://SanghyukChun.github.io/ - 2016-03-01T17:57:28+09:00 + 2016-03-06T01:29:54+09:00 http://SanghyukChun.github.io/robots.txt @@ -338,6 +342,6 @@ http://SanghyukChun.github.io/search/ - 2016-03-01T17:57:28+09:00 + 2016-03-06T01:29:54+09:00 \ No newline at end of file diff --git a/stylesheets/screen.css b/stylesheets/screen.css index 617e0b21..affa43b1 100644 --- a/stylesheets/screen.css +++ b/stylesheets/screen.css @@ -6,4 +6,4 @@ * http://www.apache.org/licenses/LICENSE-2.0 * * Designed and built with all the love in the world by @mdo and @fat. - *//* normalize.css v2.1.0 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{margin:0.67em 0;font-size:2em}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}hr{height:0;-moz-box-sizing:content-box;box-sizing:content-box}mark{color:#000;background:#ff0}code,kbd,pre,samp{font-family:monospace, serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{padding:0.35em 0.625em 0.75em;margin:0 2px;border:1px solid #c0c0c0}legend{padding:0;border:0}button,input,select,textarea{margin:0;font-family:inherit;font-size:100%}button,input{line-height:normal}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}button[disabled],html input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{padding:0;box-sizing:border-box}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}@media print{*{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:2cm .5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.table td,.table th{background-color:#fff !important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:62.5%;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;font-size:14px;line-height:1.428571429;color:#333333;background-color:#ffffff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}img{vertical-align:middle}.img-responsive{display:inline-block;height:auto;max-width:100%}.img-rounded{border-radius:6px}.img-circle{border-radius:500px}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eeeeee}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16.099999999999998px;font-weight:200;line-height:1.4}@media (min-width: 768px){.lead{font-size:21px}}small{font-size:85%}cite{font-style:normal}.text-muted{color:#999999}.text-primary{color:#428bca}.text-warning{color:#c09853}.text-danger{color:#b94a48}.text-success{color:#468847}.text-info{color:#3a87ad}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;font-weight:500;line-height:1.1}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small{font-weight:normal;line-height:1;color:#999999}h1,h2,h3{margin-top:20px;margin-bottom:10px}h4,h5,h6{margin-top:10px;margin-bottom:10px}h1,.h1{font-size:38px}h2,.h2{font-size:32px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}h1 small,.h1 small{font-size:24px}h2 small,.h2 small{font-size:18px}h3 small,.h3 small,h4 small,.h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eeeeee}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}ol{list-style-type:decimal}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-bottom:20px}dt,dd{line-height:1.428571429}dt{font-weight:bold}dd{margin-left:0}.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}.dl-horizontal dd:before,.dl-horizontal dd:after{display:table;content:" "}.dl-horizontal dd:after{clear:both}.dl-horizontal dd:before,.dl-horizontal dd:after{display:table;content:" "}.dl-horizontal dd:after{clear:both}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;border-left:5px solid #eeeeee}blockquote p{font-size:17.5px;font-weight:300;line-height:1.25}blockquote p:last-child{margin-bottom:0}blockquote small{display:block;line-height:1.428571429;color:#999999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eeeeee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:1.428571429}code,pre{font-family:Monaco, Menlo, Consolas, "Courier New", monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;white-space:nowrap;background-color:#f9f2f4;border-radius:4px}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.428571429;color:#333333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;color:inherit;white-space:pre-wrap;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto}.container:before,.container:after{display:table;content:" "}.container:after{clear:both}.container:before,.container:after{display:table;content:" "}.container:after{clear:both}.row:before,.row:after{display:table;content:" "}.row:after{clear:both}.row:before,.row:after{display:table;content:" "}.row:after{clear:both}@media (min-width: 768px){.row{margin-right:-15px;margin-left:-15px}}.row .row{margin-right:-15px;margin-left:-15px}.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12,.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12,.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12{float:left}.col-1{width:8.333333333333332%}.col-2{width:16.666666666666664%}.col-3{width:25%}.col-4{width:33.33333333333333%}.col-5{width:41.66666666666667%}.col-6{width:50%}.col-7{width:58.333333333333336%}.col-8{width:66.66666666666666%}.col-9{width:75%}.col-10{width:83.33333333333334%}.col-11{width:91.66666666666666%}.col-12{width:100%}@media (min-width: 768px){.container{max-width:728px}.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-1{width:8.333333333333332%}.col-sm-2{width:16.666666666666664%}.col-sm-3{width:25%}.col-sm-4{width:33.33333333333333%}.col-sm-5{width:41.66666666666667%}.col-sm-6{width:50%}.col-sm-7{width:58.333333333333336%}.col-sm-8{width:66.66666666666666%}.col-sm-9{width:75%}.col-sm-10{width:83.33333333333334%}.col-sm-11{width:91.66666666666666%}.col-sm-12{width:100%}.col-push-1{left:8.333333333333332%}.col-push-2{left:16.666666666666664%}.col-push-3{left:25%}.col-push-4{left:33.33333333333333%}.col-push-5{left:41.66666666666667%}.col-push-6{left:50%}.col-push-7{left:58.333333333333336%}.col-push-8{left:66.66666666666666%}.col-push-9{left:75%}.col-push-10{left:83.33333333333334%}.col-push-11{left:91.66666666666666%}.col-pull-1{right:8.333333333333332%}.col-pull-2{right:16.666666666666664%}.col-pull-3{right:25%}.col-pull-4{right:33.33333333333333%}.col-pull-5{right:41.66666666666667%}.col-pull-6{right:50%}.col-pull-7{right:58.333333333333336%}.col-pull-8{right:66.66666666666666%}.col-pull-9{right:75%}.col-pull-10{right:83.33333333333334%}.col-pull-11{right:91.66666666666666%}}@media (min-width: 992px){.container{max-width:940px}.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-1{width:8.333333333333332%}.col-lg-2{width:16.666666666666664%}.col-lg-3{width:25%}.col-lg-4{width:33.33333333333333%}.col-lg-5{width:41.66666666666667%}.col-lg-6{width:50%}.col-lg-7{width:58.333333333333336%}.col-lg-8{width:66.66666666666666%}.col-lg-9{width:75%}.col-lg-10{width:83.33333333333334%}.col-lg-11{width:91.66666666666666%}.col-lg-12{width:100%}.col-offset-1{margin-left:8.333333333333332%}.col-offset-2{margin-left:16.666666666666664%}.col-offset-3{margin-left:25%}.col-offset-4{margin-left:33.33333333333333%}.col-offset-5{margin-left:41.66666666666667%}.col-offset-6{margin-left:50%}.col-offset-7{margin-left:58.333333333333336%}.col-offset-8{margin-left:66.66666666666666%}.col-offset-9{margin-left:75%}.col-offset-10{margin-left:83.33333333333334%}.col-offset-11{margin-left:91.66666666666666%}}@media (min-width: 1200px){.container{max-width:1170px}}table{max-width:100%;background-color:transparent}th{text-align:left}.table{width:100%;margin-bottom:20px}.table thead>tr>th,.table tbody>tr>th,.table tfoot>tr>th,.table thead>tr>td,.table tbody>tr>td,.table tfoot>tr>td{padding:8px;line-height:1.428571429;vertical-align:top;border-top:1px solid #dddddd}.table thead>tr>th{vertical-align:bottom}.table caption+thead tr:first-child th,.table colgroup+thead tr:first-child th,.table thead:first-child tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #dddddd}.table .table{background-color:#ffffff}.table-condensed thead>tr>th,.table-condensed tbody>tr>th,.table-condensed tfoot>tr>th,.table-condensed thead>tr>td,.table-condensed tbody>tr>td,.table-condensed tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #dddddd}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class^="col-"]{display:table-column;float:none}table td[class^="col-"],table th[class^="col-"]{display:table-cell;float:none}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8;border-color:#d6e9c6}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede;border-color:#eed3d7}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3;border-color:#fbeed5}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td{background-color:#d0e9c6;border-color:#c9e2b3}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td{background-color:#ebcccc;border-color:#e6c1c7}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td{background-color:#faf2cc;border-color:#f8e5be}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}select[multiple],select[size]{height:auto}select optgroup{font-family:inherit;font-size:inherit;font-style:inherit}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}input[type="number"]::-webkit-outer-spin-button,input[type="number"]::-webkit-inner-spin-button{height:auto}.form-control:-moz-placeholder{color:#999999}.form-control::-moz-placeholder{color:#999999}.form-control:-ms-input-placeholder{color:#999999}.form-control::-webkit-input-placeholder{color:#999999}.form-control{display:block;width:100%;height:38px;padding:8px 12px;font-size:14px;line-height:1.428571429;color:#555555;vertical-align:middle;background-color:#ffffff;border:1px solid #cccccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;transition:border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s}.form-control:focus{border-color:rgba(82,168,236,0.8);outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eeeeee}textarea.form-control{height:auto}.form-group{margin-bottom:15px}.radio,.checkbox{display:block;min-height:20px;padding-left:20px;margin-top:10px;margin-bottom:10px;vertical-align:middle}.radio label,.checkbox label{display:inline;margin-bottom:0;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{float:left;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;font-weight:normal;vertical-align:middle;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control.input-large{height:56px;padding:14px 16px;font-size:18px;border-radius:6px}.form-control.input-small{height:30px;padding:5px 10px;font-size:12px;border-radius:3px}select.input-large{height:56px;line-height:56px}select.input-small{height:30px;line-height:30px}.has-warning .help-block,.has-warning .control-label{color:#c09853}.has-warning .form-control{padding-right:32px;border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.has-warning .input-group-addon{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.has-error .help-block,.has-error .control-label{color:#b94a48}.has-error .form-control{padding-right:32px;border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.has-error .input-group-addon{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.has-success .help-block,.has-success .control-label{color:#468847}.has-success .form-control{padding-right:32px;border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.has-success .input-group-addon{color:#468847;background-color:#dff0d8;border-color:#468847}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}.input-group{display:table;border-collapse:separate}.input-group.col{float:none;padding-right:0;padding-left:0}.input-group .form-control{width:100%;margin-bottom:0}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:8px 12px;font-size:14px;font-weight:normal;line-height:1.428571429;text-align:center;background-color:#eeeeee;border:1px solid #cccccc;border-radius:4px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-group-addon.input-small{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-large{padding:14px 16px;font-size:18px;border-radius:6px}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-4px}.input-group-btn>.btn:hover,.input-group-btn>.btn:active{z-index:2}.form-inline .form-control,.form-inline .radio,.form-inline .checkbox{display:inline-block}.form-inline .radio,.form-inline .checkbox{margin-top:0;margin-bottom:0}.form-horizontal .control-label{padding-top:6px}.form-horizontal .form-group:before,.form-horizontal .form-group:after{display:table;content:" "}.form-horizontal .form-group:after{clear:both}.form-horizontal .form-group:before,.form-horizontal .form-group:after{display:table;content:" "}.form-horizontal .form-group:after{clear:both}@media (min-width: 768px){.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}}.form-horizontal .form-group .row{margin-right:-15px;margin-left:-15px}@media (min-width: 768px){.form-horizontal .control-label{text-align:right}}.btn{display:inline-block;padding:8px 12px;margin-bottom:0;font-size:14px;font-weight:500;line-height:1.428571429;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;border:1px solid transparent;border-radius:4px}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#ffffff;text-decoration:none}.btn:active,.btn.active{outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{pointer-events:none;cursor:default;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#ffffff;background-color:#474949;border-color:#474949}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active{background-color:#3a3c3c;border-color:#2e2f2f}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#474949;border-color:#474949}.btn-primary{color:#ffffff;background-color:#428bca;border-color:#428bca}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active{background-color:#357ebd;border-color:#3071a9}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#428bca}.btn-warning{color:#ffffff;background-color:#f0ad4e;border-color:#f0ad4e}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active{background-color:#eea236;border-color:#ec971f}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#f0ad4e}.btn-danger{color:#ffffff;background-color:#d9534f;border-color:#d9534f}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active{background-color:#d43f3a;border-color:#c9302c}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d9534f}.btn-success{color:#ffffff;background-color:#5cb85c;border-color:#5cb85c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active{background-color:#4cae4c;border-color:#449d44}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#5cb85c}.btn-info{color:#ffffff;background-color:#5bc0de;border-color:#5bc0de}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active{background-color:#46b8da;border-color:#31b0d5}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#5bc0de}.btn-link{font-weight:normal;color:#428bca;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#333333;text-decoration:none}.btn-large{padding:14px 16px;font-size:18px;border-radius:6px}.btn-small{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity 0.15s linear;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height 0.35s ease;transition:height 0.35s ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid #000000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#ffffff;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.428571429;color:#333333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#ffffff;text-decoration:none;background-color:#357ebd;background-image:-webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#357ebd));background-image:-webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%);background-image:-moz-linear-gradient(top, #428bca 0%, #357ebd 100%);background-image:linear-gradient(to bottom, #428bca 0%,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#357ebd;background-image:-webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#357ebd));background-image:-webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%);background-image:-moz-linear-gradient(top, #428bca 0%, #357ebd 100%);background-image:linear-gradient(to bottom, #428bca 0%,#357ebd 100%);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0)}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.428571429;color:#999999}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.list-group{padding-left:0;margin-bottom:20px;background-color:#ffffff}.list-group-item{position:relative;display:block;padding:10px 30px 10px 15px;margin-bottom:-1px;border:1px solid #dddddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right;margin-right:-15px}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}a.list-group-item .list-group-item-heading{color:#333333}a.list-group-item .list-group-item-text{color:#555555}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;background-color:#f5f5f5}a.list-group-item.active{z-index:2;color:#ffffff;background-color:#428bca;border-color:#428bca}a.list-group-item.active .list-group-item-heading{color:inherit}a.list-group-item.active .list-group-item-text{color:#e1edf7}.panel{padding:15px;margin-bottom:20px;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-heading{padding:10px 15px;margin:-15px -15px 15px;background-color:#f5f5f5;border-bottom:1px solid #dddddd;border-top-right-radius:3px;border-top-left-radius:3px}.panel-title{margin-top:0;margin-bottom:0;font-size:17.5px;font-weight:500}.panel-footer{padding:10px 15px;margin:15px -15px -15px;background-color:#f5f5f5;border-top:1px solid #dddddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-primary{border-color:#428bca}.panel-primary .panel-heading{color:#ffffff;background-color:#428bca;border-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success .panel-heading{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.panel-warning{border-color:#fbeed5}.panel-warning .panel-heading{color:#c09853;background-color:#fcf8e3;border-color:#fbeed5}.panel-danger{border-color:#eed3d7}.panel-danger .panel-heading{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.panel-info{border-color:#bce8f1}.panel-info .panel-heading{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.list-group-flush{margin:15px -15px -15px}.list-group-flush .list-group-item{border-width:1px 0}.list-group-flush .list-group-item:first-child{border-top-right-radius:0;border-top-left-radius:0}.list-group-flush .list-group-item:last-child{border-bottom:0}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;border-radius:6px}.well-small{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000000;text-decoration:none;cursor:pointer;opacity:0.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav:before,.nav:after{display:table;content:" "}.nav:after{clear:both}.nav:before,.nav:after{display:table;content:" "}.nav:after{clear:both}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eeeeee}.nav>li.disabled>a{color:#999999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999999;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav>li+.nav-header{margin-top:9px}.nav.open>a,.nav.open>a:hover,.nav.open>a:focus{color:#ffffff;background-color:#428bca;border-color:#428bca}.nav.open>a .caret,.nav.open>a:hover .caret,.nav.open>a:focus .caret{border-top-color:#ffffff;border-bottom-color:#ffffff}.nav>.pull-right{float:right}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav-tabs{border-bottom:1px solid #dddddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.428571429;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eeeeee}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555555;cursor:default;background-color:#ffffff;border:1px solid #dddddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{display:table-cell;float:none;width:1%}.nav-tabs.nav-justified>li>a{text-align:center}.nav-tabs.nav-justified>li>a{margin-right:0;border-bottom:1px solid #dddddd}.nav-tabs.nav-justified>.active>a{border-bottom-color:#ffffff}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:5px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li>a{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{display:table-cell;float:none;width:1%}.nav-justified>li>a{text-align:center}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-bottom:1px solid #dddddd}.nav-tabs-justified>.active>a{border-bottom-color:#ffffff}.tabbable:before,.tabbable:after{display:table;content:" "}.tabbable:after{clear:both}.tabbable:before,.tabbable:after{display:table;content:" "}.tabbable:after{clear:both}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.nav .caret{border-top-color:#428bca;border-bottom-color:#428bca}.nav a:hover .caret{border-top-color:#2a6496;border-bottom-color:#2a6496}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:50px;padding-right:15px;padding-left:15px;margin-bottom:20px;background-color:#eeeeee;border-radius:4px}.navbar:before,.navbar:after{display:table;content:" "}.navbar:after{clear:both}.navbar:before,.navbar:after{display:table;content:" "}.navbar:after{clear:both}.navbar-nav{margin-top:10px;margin-bottom:15px}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px;line-height:20px;color:#777777;border-radius:4px}.navbar-nav>li>a:hover,.navbar-nav>li>a:focus{color:#333333;background-color:transparent}.navbar-nav>.active>a,.navbar-nav>.active>a:hover,.navbar-nav>.active>a:focus{color:#555555;background-color:#d5d5d5}.navbar-nav>.disabled>a,.navbar-nav>.disabled>a:hover,.navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-nav.pull-right{width:100%}.navbar-static-top{border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;border-radius:0}.navbar-fixed-top{top:0}.navbar-fixed-bottom{bottom:0;margin-bottom:0}.navbar-brand{display:block;max-width:200px;padding:15px 15px;margin-right:auto;margin-left:auto;font-size:18px;font-weight:500;line-height:20px;color:#777777;text-align:center}.navbar-brand:hover,.navbar-brand:focus{color:#5e5e5e;text-decoration:none;background-color:transparent}.navbar-toggle{position:absolute;top:9px;right:10px;width:48px;height:32px;padding:8px 12px;background-color:transparent;border:1px solid #dddddd;border-radius:4px}.navbar-toggle:hover,.navbar-toggle:focus{background-color:#dddddd}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;background-color:#cccccc;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}.navbar-form{margin-top:6px;margin-bottom:6px}.navbar-form .form-control,.navbar-form .radio,.navbar-form .checkbox{display:inline-block}.navbar-form .radio,.navbar-form .checkbox{margin-top:0;margin-bottom:0}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-nav>.dropdown>a:hover .caret,.navbar-nav>.dropdown>a:focus .caret{border-top-color:#333333;border-bottom-color:#333333}.navbar-nav>.open>a,.navbar-nav>.open>a:hover,.navbar-nav>.open>a:focus{color:#555555;background-color:#d5d5d5}.navbar-nav>.open>a .caret,.navbar-nav>.open>a:hover .caret,.navbar-nav>.open>a:focus .caret{border-top-color:#555555;border-bottom-color:#555555}.navbar-nav>.dropdown>a .caret{border-top-color:#777777;border-bottom-color:#777777}.navbar-nav.pull-right>li>.dropdown-menu,.navbar-nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar-inverse{background-color:#222222}.navbar-inverse .navbar-brand{color:#999999}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-text{color:#999999}.navbar-inverse .navbar-nav>li>a{color:#999999}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#ffffff;background-color:#080808}.navbar-inverse .navbar-nav>.dropdown>a:hover .caret{border-top-color:#ffffff;border-bottom-color:#ffffff}.navbar-inverse .navbar-nav>.dropdown>a .caret{border-top-color:#999999;border-bottom-color:#999999}.navbar-inverse .navbar-nav>.open>a .caret,.navbar-inverse .navbar-nav>.open>a:hover .caret,.navbar-inverse .navbar-nav>.open>a:focus .caret{border-top-color:#ffffff;border-bottom-color:#ffffff}@media screen and (min-width: 768px){.navbar-brand{float:left;margin-right:5px;margin-left:-15px}.navbar-nav{float:left;margin-top:0;margin-bottom:0}.navbar-nav>li{float:left}.navbar-nav>li>a{border-radius:0}.navbar-nav.pull-right{float:right;width:auto}.navbar-toggle{position:relative;top:auto;left:auto;display:none}.nav-collapse.collapse{display:block !important;height:auto !important;overflow:visible !important}}.navbar-btn{margin-top:6px}.navbar-text{margin-top:15px;margin-bottom:15px}.navbar-link{color:#777777}.navbar-link:hover{color:#333333}.navbar-inverse .navbar-link{color:#999999}.navbar-inverse .navbar-link:hover{color:#ffffff}.btn .caret{border-top-color:#ffffff}.dropup .btn .caret{border-bottom-color:#ffffff}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:active,.btn-group-vertical>.btn:active{z-index:2}.btn-group .btn+.btn{margin-left:-1px}.btn-toolbar:before,.btn-toolbar:after{display:table;content:" "}.btn-toolbar:after{clear:both}.btn-toolbar:before,.btn-toolbar:after{display:table;content:" "}.btn-toolbar:after{clear:both}.btn-toolbar .btn-group{float:left}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group,.btn-toolbar>.btn-group+.btn-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-large+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn .caret{margin-left:0}.btn-large .caret{border-width:5px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-group-vertical>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn+.btn{margin-top:-1px}.btn-group-vertical .btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical .btn:first-child{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical .btn:last-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%}.btn-group-justified .btn{display:table-cell;float:none;width:1%}.btn-group[data-toggle="buttons"]>.btn>input[type="radio"],.btn-group[data-toggle="buttons"]>.btn>input[type="checkbox"]{display:none}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#cccccc;content:"/\00a0"}.breadcrumb>.active{color:#999999}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{float:left;padding:4px 12px;line-height:1.428571429;text-decoration:none;background-color:#ffffff;border:1px solid #dddddd;border-left-width:0}.pagination>li:first-child>a,.pagination>li:first-child>span{border-left-width:1px;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:hover,.pagination>li>a:focus,.pagination>.active>a,.pagination>.active>span{background-color:#f5f5f5}.pagination>.active>a,.pagination>.active>span{color:#999999;cursor:default}.pagination>.disabled>span,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999999;cursor:not-allowed;background-color:#ffffff}.pagination-large>li>a,.pagination-large>li>span{padding:14px 16px;font-size:18px}.pagination-large>li:first-child>a,.pagination-large>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-large>li:last-child>a,.pagination-large>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-small>li>a,.pagination-small>li>span{padding:5px 10px;font-size:12px}.pagination-small>li:first-child>a,.pagination-small>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-small>li:last-child>a,.pagination-small>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager:before,.pager:after{display:table;content:" "}.pager:after{clear:both}.pager:before,.pager:after{display:table;content:" "}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#ffffff;border:1px solid #dddddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#f5f5f5}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999999;cursor:not-allowed;background-color:#ffffff}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;display:none;overflow:auto;overflow-y:scroll}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform 0.3s ease-out;-moz-transition:-moz-transform 0.3s ease-out;-o-transition:-o-transform 0.3s ease-out;transition:transform 0.3s ease-out}.modal.fade.in .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);transform:translate(0, 0)}.modal-dialog{position:relative;top:0;right:0;left:0;z-index:1050;width:auto;padding:10px}.modal-content{position:relative;background-color:#ffffff;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;outline:none;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);background-clip:padding-box}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1030;background-color:#000000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.fade.in{opacity:0.5;filter:alpha(opacity=50)}.modal-header{min-height:16.428571429px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.428571429}.modal-body{position:relative;padding:20px}.modal-footer{padding:19px 20px 20px;margin-top:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer:before,.modal-footer:after{display:table;content:" "}.modal-footer:after{clear:both}.modal-footer:before,.modal-footer:after{display:table;content:" "}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}@media screen and (min-width: 768px){.modal-dialog{right:auto;left:50%;width:560px;padding-top:30px;padding-bottom:30px;margin-left:-280px}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}}.tooltip{position:absolute;z-index:1030;display:block;font-size:12px;line-height:1.4;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:1;filter:alpha(opacity=100)}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:rgba(0,0,0,0.9);border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:rgba(0,0,0,0.9);border-width:5px 5px 0}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-top-color:rgba(0,0,0,0.9);border-width:5px 5px 0}.tooltip.top-right .tooltip-arrow{right:5px;bottom:0;border-top-color:rgba(0,0,0,0.9);border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:rgba(0,0,0,0.9);border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:rgba(0,0,0,0.9);border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:rgba(0,0,0,0.9);border-width:0 5px 5px}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-bottom-color:rgba(0,0,0,0.9);border-width:0 5px 5px}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-bottom-color:rgba(0,0,0,0.9);border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#ffffff;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);background-clip:padding-box;-webkit-bg-clip:padding-box;-moz-bg-clip:padding}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow{border-width:11px}.popover .arrow:after{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#ffffff;border-bottom-width:0;content:" "}.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#ffffff;border-left-width:0;content:" "}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#ffffff;border-top-width:0;content:" "}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#ffffff;border-right-width:0;content:" "}.alert{padding:10px 35px 10px 15px;margin-bottom:20px;color:#c09853;background-color:#fcf8e3;border:1px solid #fbeed5;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert hr{border-top-color:#f8e5be}.alert .alert-link{font-weight:500;color:#a47e3c}.alert .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#356635}.alert-danger{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger hr{border-top-color:#e6c1c7}.alert-danger .alert-link{color:#953b39}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#2d6987}.alert-block{padding-top:15px;padding-bottom:15px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.thumbnail,.img-thumbnail{padding:4px;line-height:1.428571429;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.thumbnail{display:block}.thumbnail>img,.img-thumbnail{display:inline-block;height:auto;max-width:100%}a.thumbnail:hover,a.thumbnail:focus{border-color:#428bca}.thumbnail>img{margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#333333}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.label{display:inline;padding:.25em .6em;font-size:75%;font-weight:500;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#999999;border-radius:.25em}.label[href]:hover,.label[href]:focus{color:#ffffff;text-decoration:none;cursor:pointer;background-color:#808080}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#999999;border-radius:10px}.badge:empty{display:none}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.btn .badge{position:relative;top:-1px}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#ffffff}.nav-pills>li>a>.badge{margin-left:3px}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;color:#ffffff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width 0.6s ease;transition:width 0.6s ease}.progress-striped .progress-bar{background-color:#428bca;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:40px 40px}.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-color:#d9534f;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-color:#5cb85c;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-color:#f0ad4e;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px;cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:inline-block;height:auto;max-width:100%;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);opacity:0.5;filter:alpha(opacity=50)}.carousel-control.left{background-color:rgba(0,0,0,0.0001);background-color:transparent;background-image:-webkit-gradient(linear, 0 top, 100% top, from(rgba(0,0,0,0.5)), to(rgba(0,0,0,0.0001)));background-image:-webkit-linear-gradient(left, color-stop(rgba(0,0,0,0.5) 0), color-stop(rgba(0,0,0,0.0001) 100%));background-image:-moz-linear-gradient(left, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0%,rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{right:0;left:auto;background-color:rgba(0,0,0,0.5);background-color:transparent;background-image:-webkit-gradient(linear, 0 top, 100% top, from(rgba(0,0,0,0.0001)), to(rgba(0,0,0,0.5)));background-image:-webkit-linear-gradient(left, color-stop(rgba(0,0,0,0.0001) 0), color-stop(rgba(0,0,0,0.5) 100%));background-image:-moz-linear-gradient(left, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0%,rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90)}.carousel-control .glyphicon,.carousel-control .icon-prev,.carousel-control .icon-next{position:absolute;top:50%;left:50%;z-index:5;display:inline-block;width:20px;height:20px;margin-top:-10px;margin-left:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:120px;padding-left:0;margin-left:-60px;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width: 768px){.carousel-control .glyphicon,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;margin-left:-15px;font-size:30px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.jumbotron{padding:30px;margin-bottom:30px;font-size:21px;font-weight:200;line-height:2.1428571435;color:inherit;background-color:#eeeeee}.jumbotron h1{line-height:1;color:inherit}.jumbotron p{line-height:1.4}@media screen and (min-width: 768px){.jumbotron{padding:50px 60px;border-radius:6px}.jumbotron h1{font-size:63px}}.clearfix:before,.clearfix:after{display:table;content:" "}.clearfix:after{clear:both}.pull-right{float:right}.pull-left{float:left}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.affix{position:fixed}@-ms-viewport{width:device-width}@media screen and (max-width: 400px){@-ms-viewport{width:320px}}.hidden{display:none !important;visibility:hidden !important}.visible-sm{display:block !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}.visible-md{display:none !important}tr.visible-md{display:none !important}th.visible-md,td.visible-md{display:none !important}.visible-lg{display:none !important}tr.visible-lg{display:none !important}th.visible-lg,td.visible-lg{display:none !important}.hidden-sm{display:none !important}tr.hidden-sm{display:none !important}th.hidden-sm,td.hidden-sm{display:none !important}.hidden-md{display:block !important}tr.hidden-md{display:table-row !important}th.hidden-md,td.hidden-md{display:table-cell !important}.hidden-lg{display:block !important}tr.hidden-lg{display:table-row !important}th.hidden-lg,td.hidden-lg{display:table-cell !important}@media (min-width: 768px) and (max-width: 991px){.visible-sm{display:none !important}tr.visible-sm{display:none !important}th.visible-sm,td.visible-sm{display:none !important}.visible-md{display:block !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}.visible-lg{display:none !important}tr.visible-lg{display:none !important}th.visible-lg,td.visible-lg{display:none !important}.hidden-sm{display:block !important}tr.hidden-sm{display:table-row !important}th.hidden-sm,td.hidden-sm{display:table-cell !important}.hidden-md{display:none !important}tr.hidden-md{display:none !important}th.hidden-md,td.hidden-md{display:none !important}.hidden-lg{display:block !important}tr.hidden-lg{display:table-row !important}th.hidden-lg,td.hidden-lg{display:table-cell !important}}@media (min-width: 992px){.visible-sm{display:none !important}tr.visible-sm{display:none !important}th.visible-sm,td.visible-sm{display:none !important}.visible-md{display:none !important}tr.visible-md{display:none !important}th.visible-md,td.visible-md{display:none !important}.visible-lg{display:block !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}.hidden-sm{display:block !important}tr.hidden-sm{display:table-row !important}th.hidden-sm,td.hidden-sm{display:table-cell !important}.hidden-md{display:block !important}tr.hidden-md{display:table-row !important}th.hidden-md,td.hidden-md{display:table-cell !important}.hidden-lg{display:none !important}tr.hidden-lg{display:none !important}th.hidden-lg,td.hidden-lg{display:none !important}}.visible-print{display:none !important}tr.visible-print{display:none !important}th.visible-print,td.visible-print{display:none !important}@media print{.visible-print{display:block !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}.hidden-print{display:none !important}tr.hidden-print{display:none !important}th.hidden-print,td.hidden-print{display:none !important}}#blog-title{color:black;text-align:left;font-size:1.8em;font-weight:600}div#main{width:740px;margin:0 auto;text-align:justify}body{text-align:center}body>header{margin:0 auto}footer{margin-top:40px;margin-bottom:50px;font-size:1.2em;font-weight:500}footer-temp{border-top:1px solid #e8e8e8;padding:30px 0px}span.archive-title{font-size:1.1em}h1{font-size:1.5em;font-weight:700}h2{font-size:1.35em}h3,h4,h5{font-size:1.2em}h5{margin-top:1.5em}h6{font-size:1em}h1.entry-title>a{color:black}h1.archive-post>a{color:black}h2#archive-title{font-size:1.5em;color:#A00000}a:hover{text-decoration:none}a.red{color:#A00000}a.url-title{font-size:0.8em}a.prev{margin-right:20px}.categories>a{color:#A00000;font-size:0.85em}.white{color:#FFF}div.entry-content{padding-bottom:60px}ul.main-navigation{text-align:left;padding-bottom:35px}ul.main-navigation>li{padding-right:30px;font-family:"Nanum Gothic Coding"}ul.main-navigation>li:after{content:" /"}ul.main-navigation>li>a{color:black}ul.main-navigation>li>a:hover{color:#A00000}.mB50{margin-bottom:50px}.mB40{margin-bottom:40px}.mT30{margin-top:30px}.m20{margin:20px 0px}.clear{clear:both}.sharing>div.sh_button{float:left;padding:4px}img{margin-top:10px;margin-bottom:10px}img.center{display:block;margin-left:auto;margin-right:auto}html{font-family:"Nanum Gothic Coding"}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Nanum Gothic"}body{font-family:"Nanum Gothic"}code,pre{font-family:"Nanum Gothic Coding"}select optgroup{font-family:"Nanum Gothic"}.footnote{vertical-align:super;font-size:50%}ul{list-style:disc}ul.mL10>li{margin-left:10px} + *//* normalize.css v2.1.0 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{margin:0.67em 0;font-size:2em}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}hr{height:0;-moz-box-sizing:content-box;box-sizing:content-box}mark{color:#000;background:#ff0}code,kbd,pre,samp{font-family:monospace, serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{padding:0.35em 0.625em 0.75em;margin:0 2px;border:1px solid #c0c0c0}legend{padding:0;border:0}button,input,select,textarea{margin:0;font-family:inherit;font-size:100%}button,input{line-height:normal}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}button[disabled],html input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{padding:0;box-sizing:border-box}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}@media print{*{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:2cm .5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.table td,.table th{background-color:#fff !important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:62.5%;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;font-size:14px;line-height:1.428571429;color:#333333;background-color:#ffffff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}img{vertical-align:middle}.img-responsive{display:inline-block;height:auto;max-width:100%}.img-rounded{border-radius:6px}.img-circle{border-radius:500px}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eeeeee}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16.099999999999998px;font-weight:200;line-height:1.4}@media (min-width: 768px){.lead{font-size:21px}}small{font-size:85%}cite{font-style:normal}.text-muted{color:#999999}.text-primary{color:#428bca}.text-warning{color:#c09853}.text-danger{color:#b94a48}.text-success{color:#468847}.text-info{color:#3a87ad}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;font-weight:500;line-height:1.1}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small{font-weight:normal;line-height:1;color:#999999}h1,h2,h3{margin-top:20px;margin-bottom:10px}h4,h5,h6{margin-top:10px;margin-bottom:10px}h1,.h1{font-size:38px}h2,.h2{font-size:32px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}h1 small,.h1 small{font-size:24px}h2 small,.h2 small{font-size:18px}h3 small,.h3 small,h4 small,.h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eeeeee}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}ol{list-style-type:decimal}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-bottom:20px}dt,dd{line-height:1.428571429}dt{font-weight:bold}dd{margin-left:0}.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}.dl-horizontal dd:before,.dl-horizontal dd:after{display:table;content:" "}.dl-horizontal dd:after{clear:both}.dl-horizontal dd:before,.dl-horizontal dd:after{display:table;content:" "}.dl-horizontal dd:after{clear:both}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;border-left:5px solid #eeeeee}blockquote p{font-size:17.5px;font-weight:300;line-height:1.25}blockquote p:last-child{margin-bottom:0}blockquote small{display:block;line-height:1.428571429;color:#999999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eeeeee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:1.428571429}code,pre{font-family:Monaco, Menlo, Consolas, "Courier New", monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;white-space:nowrap;background-color:#f9f2f4;border-radius:4px}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.428571429;color:#333333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;color:inherit;white-space:pre-wrap;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto}.container:before,.container:after{display:table;content:" "}.container:after{clear:both}.container:before,.container:after{display:table;content:" "}.container:after{clear:both}.row:before,.row:after{display:table;content:" "}.row:after{clear:both}.row:before,.row:after{display:table;content:" "}.row:after{clear:both}@media (min-width: 768px){.row{margin-right:-15px;margin-left:-15px}}.row .row{margin-right:-15px;margin-left:-15px}.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12,.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12,.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12{float:left}.col-1{width:8.333333333333332%}.col-2{width:16.666666666666664%}.col-3{width:25%}.col-4{width:33.33333333333333%}.col-5{width:41.66666666666667%}.col-6{width:50%}.col-7{width:58.333333333333336%}.col-8{width:66.66666666666666%}.col-9{width:75%}.col-10{width:83.33333333333334%}.col-11{width:91.66666666666666%}.col-12{width:100%}@media (min-width: 768px){.container{max-width:728px}.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-1{width:8.333333333333332%}.col-sm-2{width:16.666666666666664%}.col-sm-3{width:25%}.col-sm-4{width:33.33333333333333%}.col-sm-5{width:41.66666666666667%}.col-sm-6{width:50%}.col-sm-7{width:58.333333333333336%}.col-sm-8{width:66.66666666666666%}.col-sm-9{width:75%}.col-sm-10{width:83.33333333333334%}.col-sm-11{width:91.66666666666666%}.col-sm-12{width:100%}.col-push-1{left:8.333333333333332%}.col-push-2{left:16.666666666666664%}.col-push-3{left:25%}.col-push-4{left:33.33333333333333%}.col-push-5{left:41.66666666666667%}.col-push-6{left:50%}.col-push-7{left:58.333333333333336%}.col-push-8{left:66.66666666666666%}.col-push-9{left:75%}.col-push-10{left:83.33333333333334%}.col-push-11{left:91.66666666666666%}.col-pull-1{right:8.333333333333332%}.col-pull-2{right:16.666666666666664%}.col-pull-3{right:25%}.col-pull-4{right:33.33333333333333%}.col-pull-5{right:41.66666666666667%}.col-pull-6{right:50%}.col-pull-7{right:58.333333333333336%}.col-pull-8{right:66.66666666666666%}.col-pull-9{right:75%}.col-pull-10{right:83.33333333333334%}.col-pull-11{right:91.66666666666666%}}@media (min-width: 992px){.container{max-width:940px}.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-1{width:8.333333333333332%}.col-lg-2{width:16.666666666666664%}.col-lg-3{width:25%}.col-lg-4{width:33.33333333333333%}.col-lg-5{width:41.66666666666667%}.col-lg-6{width:50%}.col-lg-7{width:58.333333333333336%}.col-lg-8{width:66.66666666666666%}.col-lg-9{width:75%}.col-lg-10{width:83.33333333333334%}.col-lg-11{width:91.66666666666666%}.col-lg-12{width:100%}.col-offset-1{margin-left:8.333333333333332%}.col-offset-2{margin-left:16.666666666666664%}.col-offset-3{margin-left:25%}.col-offset-4{margin-left:33.33333333333333%}.col-offset-5{margin-left:41.66666666666667%}.col-offset-6{margin-left:50%}.col-offset-7{margin-left:58.333333333333336%}.col-offset-8{margin-left:66.66666666666666%}.col-offset-9{margin-left:75%}.col-offset-10{margin-left:83.33333333333334%}.col-offset-11{margin-left:91.66666666666666%}}@media (min-width: 1200px){.container{max-width:1170px}}table{max-width:100%;background-color:transparent}th{text-align:left}.table{width:100%;margin-bottom:20px}.table thead>tr>th,.table tbody>tr>th,.table tfoot>tr>th,.table thead>tr>td,.table tbody>tr>td,.table tfoot>tr>td{padding:8px;line-height:1.428571429;vertical-align:top;border-top:1px solid #dddddd}.table thead>tr>th{vertical-align:bottom}.table caption+thead tr:first-child th,.table colgroup+thead tr:first-child th,.table thead:first-child tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #dddddd}.table .table{background-color:#ffffff}.table-condensed thead>tr>th,.table-condensed tbody>tr>th,.table-condensed tfoot>tr>th,.table-condensed thead>tr>td,.table-condensed tbody>tr>td,.table-condensed tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #dddddd}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class^="col-"]{display:table-column;float:none}table td[class^="col-"],table th[class^="col-"]{display:table-cell;float:none}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8;border-color:#d6e9c6}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede;border-color:#eed3d7}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3;border-color:#fbeed5}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td{background-color:#d0e9c6;border-color:#c9e2b3}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td{background-color:#ebcccc;border-color:#e6c1c7}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td{background-color:#faf2cc;border-color:#f8e5be}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}select[multiple],select[size]{height:auto}select optgroup{font-family:inherit;font-size:inherit;font-style:inherit}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}input[type="number"]::-webkit-outer-spin-button,input[type="number"]::-webkit-inner-spin-button{height:auto}.form-control:-moz-placeholder{color:#999999}.form-control::-moz-placeholder{color:#999999}.form-control:-ms-input-placeholder{color:#999999}.form-control::-webkit-input-placeholder{color:#999999}.form-control{display:block;width:100%;height:38px;padding:8px 12px;font-size:14px;line-height:1.428571429;color:#555555;vertical-align:middle;background-color:#ffffff;border:1px solid #cccccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;transition:border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s}.form-control:focus{border-color:rgba(82,168,236,0.8);outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eeeeee}textarea.form-control{height:auto}.form-group{margin-bottom:15px}.radio,.checkbox{display:block;min-height:20px;padding-left:20px;margin-top:10px;margin-bottom:10px;vertical-align:middle}.radio label,.checkbox label{display:inline;margin-bottom:0;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{float:left;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;font-weight:normal;vertical-align:middle;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control.input-large{height:56px;padding:14px 16px;font-size:18px;border-radius:6px}.form-control.input-small{height:30px;padding:5px 10px;font-size:12px;border-radius:3px}select.input-large{height:56px;line-height:56px}select.input-small{height:30px;line-height:30px}.has-warning .help-block,.has-warning .control-label{color:#c09853}.has-warning .form-control{padding-right:32px;border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.has-warning .input-group-addon{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.has-error .help-block,.has-error .control-label{color:#b94a48}.has-error .form-control{padding-right:32px;border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.has-error .input-group-addon{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.has-success .help-block,.has-success .control-label{color:#468847}.has-success .form-control{padding-right:32px;border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.has-success .input-group-addon{color:#468847;background-color:#dff0d8;border-color:#468847}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}.input-group{display:table;border-collapse:separate}.input-group.col{float:none;padding-right:0;padding-left:0}.input-group .form-control{width:100%;margin-bottom:0}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:8px 12px;font-size:14px;font-weight:normal;line-height:1.428571429;text-align:center;background-color:#eeeeee;border:1px solid #cccccc;border-radius:4px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-group-addon.input-small{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-large{padding:14px 16px;font-size:18px;border-radius:6px}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-4px}.input-group-btn>.btn:hover,.input-group-btn>.btn:active{z-index:2}.form-inline .form-control,.form-inline .radio,.form-inline .checkbox{display:inline-block}.form-inline .radio,.form-inline .checkbox{margin-top:0;margin-bottom:0}.form-horizontal .control-label{padding-top:6px}.form-horizontal .form-group:before,.form-horizontal .form-group:after{display:table;content:" "}.form-horizontal .form-group:after{clear:both}.form-horizontal .form-group:before,.form-horizontal .form-group:after{display:table;content:" "}.form-horizontal .form-group:after{clear:both}@media (min-width: 768px){.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}}.form-horizontal .form-group .row{margin-right:-15px;margin-left:-15px}@media (min-width: 768px){.form-horizontal .control-label{text-align:right}}.btn{display:inline-block;padding:8px 12px;margin-bottom:0;font-size:14px;font-weight:500;line-height:1.428571429;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;border:1px solid transparent;border-radius:4px}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#ffffff;text-decoration:none}.btn:active,.btn.active{outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{pointer-events:none;cursor:default;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#ffffff;background-color:#474949;border-color:#474949}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active{background-color:#3a3c3c;border-color:#2e2f2f}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#474949;border-color:#474949}.btn-primary{color:#ffffff;background-color:#428bca;border-color:#428bca}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active{background-color:#357ebd;border-color:#3071a9}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#428bca}.btn-warning{color:#ffffff;background-color:#f0ad4e;border-color:#f0ad4e}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active{background-color:#eea236;border-color:#ec971f}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#f0ad4e}.btn-danger{color:#ffffff;background-color:#d9534f;border-color:#d9534f}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active{background-color:#d43f3a;border-color:#c9302c}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d9534f}.btn-success{color:#ffffff;background-color:#5cb85c;border-color:#5cb85c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active{background-color:#4cae4c;border-color:#449d44}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#5cb85c}.btn-info{color:#ffffff;background-color:#5bc0de;border-color:#5bc0de}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active{background-color:#46b8da;border-color:#31b0d5}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#5bc0de}.btn-link{font-weight:normal;color:#428bca;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#333333;text-decoration:none}.btn-large{padding:14px 16px;font-size:18px;border-radius:6px}.btn-small{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity 0.15s linear;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height 0.35s ease;transition:height 0.35s ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid #000000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#ffffff;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.428571429;color:#333333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#ffffff;text-decoration:none;background-color:#357ebd;background-image:-webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#357ebd));background-image:-webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%);background-image:-moz-linear-gradient(top, #428bca 0%, #357ebd 100%);background-image:linear-gradient(to bottom, #428bca 0%,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#357ebd;background-image:-webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#357ebd));background-image:-webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%);background-image:-moz-linear-gradient(top, #428bca 0%, #357ebd 100%);background-image:linear-gradient(to bottom, #428bca 0%,#357ebd 100%);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0)}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.428571429;color:#999999}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.list-group{padding-left:0;margin-bottom:20px;background-color:#ffffff}.list-group-item{position:relative;display:block;padding:10px 30px 10px 15px;margin-bottom:-1px;border:1px solid #dddddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right;margin-right:-15px}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}a.list-group-item .list-group-item-heading{color:#333333}a.list-group-item .list-group-item-text{color:#555555}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;background-color:#f5f5f5}a.list-group-item.active{z-index:2;color:#ffffff;background-color:#428bca;border-color:#428bca}a.list-group-item.active .list-group-item-heading{color:inherit}a.list-group-item.active .list-group-item-text{color:#e1edf7}.panel{padding:15px;margin-bottom:20px;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-heading{padding:10px 15px;margin:-15px -15px 15px;background-color:#f5f5f5;border-bottom:1px solid #dddddd;border-top-right-radius:3px;border-top-left-radius:3px}.panel-title{margin-top:0;margin-bottom:0;font-size:17.5px;font-weight:500}.panel-footer{padding:10px 15px;margin:15px -15px -15px;background-color:#f5f5f5;border-top:1px solid #dddddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-primary{border-color:#428bca}.panel-primary .panel-heading{color:#ffffff;background-color:#428bca;border-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success .panel-heading{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.panel-warning{border-color:#fbeed5}.panel-warning .panel-heading{color:#c09853;background-color:#fcf8e3;border-color:#fbeed5}.panel-danger{border-color:#eed3d7}.panel-danger .panel-heading{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.panel-info{border-color:#bce8f1}.panel-info .panel-heading{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.list-group-flush{margin:15px -15px -15px}.list-group-flush .list-group-item{border-width:1px 0}.list-group-flush .list-group-item:first-child{border-top-right-radius:0;border-top-left-radius:0}.list-group-flush .list-group-item:last-child{border-bottom:0}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;border-radius:6px}.well-small{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000000;text-decoration:none;cursor:pointer;opacity:0.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav:before,.nav:after{display:table;content:" "}.nav:after{clear:both}.nav:before,.nav:after{display:table;content:" "}.nav:after{clear:both}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eeeeee}.nav>li.disabled>a{color:#999999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999999;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav>li+.nav-header{margin-top:9px}.nav.open>a,.nav.open>a:hover,.nav.open>a:focus{color:#ffffff;background-color:#428bca;border-color:#428bca}.nav.open>a .caret,.nav.open>a:hover .caret,.nav.open>a:focus .caret{border-top-color:#ffffff;border-bottom-color:#ffffff}.nav>.pull-right{float:right}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav-tabs{border-bottom:1px solid #dddddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.428571429;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eeeeee}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555555;cursor:default;background-color:#ffffff;border:1px solid #dddddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{display:table-cell;float:none;width:1%}.nav-tabs.nav-justified>li>a{text-align:center}.nav-tabs.nav-justified>li>a{margin-right:0;border-bottom:1px solid #dddddd}.nav-tabs.nav-justified>.active>a{border-bottom-color:#ffffff}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:5px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li>a{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{display:table-cell;float:none;width:1%}.nav-justified>li>a{text-align:center}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-bottom:1px solid #dddddd}.nav-tabs-justified>.active>a{border-bottom-color:#ffffff}.tabbable:before,.tabbable:after{display:table;content:" "}.tabbable:after{clear:both}.tabbable:before,.tabbable:after{display:table;content:" "}.tabbable:after{clear:both}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.nav .caret{border-top-color:#428bca;border-bottom-color:#428bca}.nav a:hover .caret{border-top-color:#2a6496;border-bottom-color:#2a6496}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:50px;padding-right:15px;padding-left:15px;margin-bottom:20px;background-color:#eeeeee;border-radius:4px}.navbar:before,.navbar:after{display:table;content:" "}.navbar:after{clear:both}.navbar:before,.navbar:after{display:table;content:" "}.navbar:after{clear:both}.navbar-nav{margin-top:10px;margin-bottom:15px}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px;line-height:20px;color:#777777;border-radius:4px}.navbar-nav>li>a:hover,.navbar-nav>li>a:focus{color:#333333;background-color:transparent}.navbar-nav>.active>a,.navbar-nav>.active>a:hover,.navbar-nav>.active>a:focus{color:#555555;background-color:#d5d5d5}.navbar-nav>.disabled>a,.navbar-nav>.disabled>a:hover,.navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-nav.pull-right{width:100%}.navbar-static-top{border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;border-radius:0}.navbar-fixed-top{top:0}.navbar-fixed-bottom{bottom:0;margin-bottom:0}.navbar-brand{display:block;max-width:200px;padding:15px 15px;margin-right:auto;margin-left:auto;font-size:18px;font-weight:500;line-height:20px;color:#777777;text-align:center}.navbar-brand:hover,.navbar-brand:focus{color:#5e5e5e;text-decoration:none;background-color:transparent}.navbar-toggle{position:absolute;top:9px;right:10px;width:48px;height:32px;padding:8px 12px;background-color:transparent;border:1px solid #dddddd;border-radius:4px}.navbar-toggle:hover,.navbar-toggle:focus{background-color:#dddddd}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;background-color:#cccccc;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}.navbar-form{margin-top:6px;margin-bottom:6px}.navbar-form .form-control,.navbar-form .radio,.navbar-form .checkbox{display:inline-block}.navbar-form .radio,.navbar-form .checkbox{margin-top:0;margin-bottom:0}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-nav>.dropdown>a:hover .caret,.navbar-nav>.dropdown>a:focus .caret{border-top-color:#333333;border-bottom-color:#333333}.navbar-nav>.open>a,.navbar-nav>.open>a:hover,.navbar-nav>.open>a:focus{color:#555555;background-color:#d5d5d5}.navbar-nav>.open>a .caret,.navbar-nav>.open>a:hover .caret,.navbar-nav>.open>a:focus .caret{border-top-color:#555555;border-bottom-color:#555555}.navbar-nav>.dropdown>a .caret{border-top-color:#777777;border-bottom-color:#777777}.navbar-nav.pull-right>li>.dropdown-menu,.navbar-nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar-inverse{background-color:#222222}.navbar-inverse .navbar-brand{color:#999999}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-text{color:#999999}.navbar-inverse .navbar-nav>li>a{color:#999999}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#ffffff;background-color:#080808}.navbar-inverse .navbar-nav>.dropdown>a:hover .caret{border-top-color:#ffffff;border-bottom-color:#ffffff}.navbar-inverse .navbar-nav>.dropdown>a .caret{border-top-color:#999999;border-bottom-color:#999999}.navbar-inverse .navbar-nav>.open>a .caret,.navbar-inverse .navbar-nav>.open>a:hover .caret,.navbar-inverse .navbar-nav>.open>a:focus .caret{border-top-color:#ffffff;border-bottom-color:#ffffff}@media screen and (min-width: 768px){.navbar-brand{float:left;margin-right:5px;margin-left:-15px}.navbar-nav{float:left;margin-top:0;margin-bottom:0}.navbar-nav>li{float:left}.navbar-nav>li>a{border-radius:0}.navbar-nav.pull-right{float:right;width:auto}.navbar-toggle{position:relative;top:auto;left:auto;display:none}.nav-collapse.collapse{display:block !important;height:auto !important;overflow:visible !important}}.navbar-btn{margin-top:6px}.navbar-text{margin-top:15px;margin-bottom:15px}.navbar-link{color:#777777}.navbar-link:hover{color:#333333}.navbar-inverse .navbar-link{color:#999999}.navbar-inverse .navbar-link:hover{color:#ffffff}.btn .caret{border-top-color:#ffffff}.dropup .btn .caret{border-bottom-color:#ffffff}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:active,.btn-group-vertical>.btn:active{z-index:2}.btn-group .btn+.btn{margin-left:-1px}.btn-toolbar:before,.btn-toolbar:after{display:table;content:" "}.btn-toolbar:after{clear:both}.btn-toolbar:before,.btn-toolbar:after{display:table;content:" "}.btn-toolbar:after{clear:both}.btn-toolbar .btn-group{float:left}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group,.btn-toolbar>.btn-group+.btn-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-large+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn .caret{margin-left:0}.btn-large .caret{border-width:5px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-group-vertical>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn+.btn{margin-top:-1px}.btn-group-vertical .btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical .btn:first-child{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical .btn:last-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%}.btn-group-justified .btn{display:table-cell;float:none;width:1%}.btn-group[data-toggle="buttons"]>.btn>input[type="radio"],.btn-group[data-toggle="buttons"]>.btn>input[type="checkbox"]{display:none}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#cccccc;content:"/\00a0"}.breadcrumb>.active{color:#999999}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{float:left;padding:4px 12px;line-height:1.428571429;text-decoration:none;background-color:#ffffff;border:1px solid #dddddd;border-left-width:0}.pagination>li:first-child>a,.pagination>li:first-child>span{border-left-width:1px;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:hover,.pagination>li>a:focus,.pagination>.active>a,.pagination>.active>span{background-color:#f5f5f5}.pagination>.active>a,.pagination>.active>span{color:#999999;cursor:default}.pagination>.disabled>span,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999999;cursor:not-allowed;background-color:#ffffff}.pagination-large>li>a,.pagination-large>li>span{padding:14px 16px;font-size:18px}.pagination-large>li:first-child>a,.pagination-large>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-large>li:last-child>a,.pagination-large>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-small>li>a,.pagination-small>li>span{padding:5px 10px;font-size:12px}.pagination-small>li:first-child>a,.pagination-small>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-small>li:last-child>a,.pagination-small>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager:before,.pager:after{display:table;content:" "}.pager:after{clear:both}.pager:before,.pager:after{display:table;content:" "}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#ffffff;border:1px solid #dddddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#f5f5f5}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999999;cursor:not-allowed;background-color:#ffffff}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;display:none;overflow:auto;overflow-y:scroll}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform 0.3s ease-out;-moz-transition:-moz-transform 0.3s ease-out;-o-transition:-o-transform 0.3s ease-out;transition:transform 0.3s ease-out}.modal.fade.in .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);transform:translate(0, 0)}.modal-dialog{position:relative;top:0;right:0;left:0;z-index:1050;width:auto;padding:10px}.modal-content{position:relative;background-color:#ffffff;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;outline:none;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);background-clip:padding-box}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1030;background-color:#000000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.fade.in{opacity:0.5;filter:alpha(opacity=50)}.modal-header{min-height:16.428571429px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.428571429}.modal-body{position:relative;padding:20px}.modal-footer{padding:19px 20px 20px;margin-top:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer:before,.modal-footer:after{display:table;content:" "}.modal-footer:after{clear:both}.modal-footer:before,.modal-footer:after{display:table;content:" "}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}@media screen and (min-width: 768px){.modal-dialog{right:auto;left:50%;width:560px;padding-top:30px;padding-bottom:30px;margin-left:-280px}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}}.tooltip{position:absolute;z-index:1030;display:block;font-size:12px;line-height:1.4;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:1;filter:alpha(opacity=100)}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:rgba(0,0,0,0.9);border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:rgba(0,0,0,0.9);border-width:5px 5px 0}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-top-color:rgba(0,0,0,0.9);border-width:5px 5px 0}.tooltip.top-right .tooltip-arrow{right:5px;bottom:0;border-top-color:rgba(0,0,0,0.9);border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:rgba(0,0,0,0.9);border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:rgba(0,0,0,0.9);border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:rgba(0,0,0,0.9);border-width:0 5px 5px}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-bottom-color:rgba(0,0,0,0.9);border-width:0 5px 5px}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-bottom-color:rgba(0,0,0,0.9);border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#ffffff;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);background-clip:padding-box;-webkit-bg-clip:padding-box;-moz-bg-clip:padding}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow{border-width:11px}.popover .arrow:after{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#ffffff;border-bottom-width:0;content:" "}.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#ffffff;border-left-width:0;content:" "}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#ffffff;border-top-width:0;content:" "}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#ffffff;border-right-width:0;content:" "}.alert{padding:10px 35px 10px 15px;margin-bottom:20px;color:#c09853;background-color:#fcf8e3;border:1px solid #fbeed5;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert hr{border-top-color:#f8e5be}.alert .alert-link{font-weight:500;color:#a47e3c}.alert .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#356635}.alert-danger{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger hr{border-top-color:#e6c1c7}.alert-danger .alert-link{color:#953b39}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#2d6987}.alert-block{padding-top:15px;padding-bottom:15px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.thumbnail,.img-thumbnail{padding:4px;line-height:1.428571429;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.thumbnail{display:block}.thumbnail>img,.img-thumbnail{display:inline-block;height:auto;max-width:100%}a.thumbnail:hover,a.thumbnail:focus{border-color:#428bca}.thumbnail>img{margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#333333}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.label{display:inline;padding:.25em .6em;font-size:75%;font-weight:500;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#999999;border-radius:.25em}.label[href]:hover,.label[href]:focus{color:#ffffff;text-decoration:none;cursor:pointer;background-color:#808080}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#999999;border-radius:10px}.badge:empty{display:none}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.btn .badge{position:relative;top:-1px}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#ffffff}.nav-pills>li>a>.badge{margin-left:3px}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;color:#ffffff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width 0.6s ease;transition:width 0.6s ease}.progress-striped .progress-bar{background-color:#428bca;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:40px 40px}.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-color:#d9534f;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-color:#5cb85c;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-color:#f0ad4e;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px;cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:inline-block;height:auto;max-width:100%;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);opacity:0.5;filter:alpha(opacity=50)}.carousel-control.left{background-color:rgba(0,0,0,0.0001);background-color:transparent;background-image:-webkit-gradient(linear, 0 top, 100% top, from(rgba(0,0,0,0.5)), to(rgba(0,0,0,0.0001)));background-image:-webkit-linear-gradient(left, color-stop(rgba(0,0,0,0.5) 0), color-stop(rgba(0,0,0,0.0001) 100%));background-image:-moz-linear-gradient(left, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0%,rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{right:0;left:auto;background-color:rgba(0,0,0,0.5);background-color:transparent;background-image:-webkit-gradient(linear, 0 top, 100% top, from(rgba(0,0,0,0.0001)), to(rgba(0,0,0,0.5)));background-image:-webkit-linear-gradient(left, color-stop(rgba(0,0,0,0.0001) 0), color-stop(rgba(0,0,0,0.5) 100%));background-image:-moz-linear-gradient(left, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0%,rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90)}.carousel-control .glyphicon,.carousel-control .icon-prev,.carousel-control .icon-next{position:absolute;top:50%;left:50%;z-index:5;display:inline-block;width:20px;height:20px;margin-top:-10px;margin-left:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:120px;padding-left:0;margin-left:-60px;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width: 768px){.carousel-control .glyphicon,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;margin-left:-15px;font-size:30px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.jumbotron{padding:30px;margin-bottom:30px;font-size:21px;font-weight:200;line-height:2.1428571435;color:inherit;background-color:#eeeeee}.jumbotron h1{line-height:1;color:inherit}.jumbotron p{line-height:1.4}@media screen and (min-width: 768px){.jumbotron{padding:50px 60px;border-radius:6px}.jumbotron h1{font-size:63px}}.clearfix:before,.clearfix:after{display:table;content:" "}.clearfix:after{clear:both}.pull-right{float:right}.pull-left{float:left}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.affix{position:fixed}@-ms-viewport{width:device-width}@media screen and (max-width: 400px){@-ms-viewport{width:320px}}.hidden{display:none !important;visibility:hidden !important}.visible-sm{display:block !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}.visible-md{display:none !important}tr.visible-md{display:none !important}th.visible-md,td.visible-md{display:none !important}.visible-lg{display:none !important}tr.visible-lg{display:none !important}th.visible-lg,td.visible-lg{display:none !important}.hidden-sm{display:none !important}tr.hidden-sm{display:none !important}th.hidden-sm,td.hidden-sm{display:none !important}.hidden-md{display:block !important}tr.hidden-md{display:table-row !important}th.hidden-md,td.hidden-md{display:table-cell !important}.hidden-lg{display:block !important}tr.hidden-lg{display:table-row !important}th.hidden-lg,td.hidden-lg{display:table-cell !important}@media (min-width: 768px) and (max-width: 991px){.visible-sm{display:none !important}tr.visible-sm{display:none !important}th.visible-sm,td.visible-sm{display:none !important}.visible-md{display:block !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}.visible-lg{display:none !important}tr.visible-lg{display:none !important}th.visible-lg,td.visible-lg{display:none !important}.hidden-sm{display:block !important}tr.hidden-sm{display:table-row !important}th.hidden-sm,td.hidden-sm{display:table-cell !important}.hidden-md{display:none !important}tr.hidden-md{display:none !important}th.hidden-md,td.hidden-md{display:none !important}.hidden-lg{display:block !important}tr.hidden-lg{display:table-row !important}th.hidden-lg,td.hidden-lg{display:table-cell !important}}@media (min-width: 992px){.visible-sm{display:none !important}tr.visible-sm{display:none !important}th.visible-sm,td.visible-sm{display:none !important}.visible-md{display:none !important}tr.visible-md{display:none !important}th.visible-md,td.visible-md{display:none !important}.visible-lg{display:block !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}.hidden-sm{display:block !important}tr.hidden-sm{display:table-row !important}th.hidden-sm,td.hidden-sm{display:table-cell !important}.hidden-md{display:block !important}tr.hidden-md{display:table-row !important}th.hidden-md,td.hidden-md{display:table-cell !important}.hidden-lg{display:none !important}tr.hidden-lg{display:none !important}th.hidden-lg,td.hidden-lg{display:none !important}}.visible-print{display:none !important}tr.visible-print{display:none !important}th.visible-print,td.visible-print{display:none !important}@media print{.visible-print{display:block !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}.hidden-print{display:none !important}tr.hidden-print{display:none !important}th.hidden-print,td.hidden-print{display:none !important}}#blog-title{color:black;text-align:left;font-size:1.8em;font-weight:600}div#main{width:740px;margin:0 auto;text-align:justify}body{text-align:center}body>header{margin:0 auto}footer{margin-top:40px;margin-bottom:50px;font-size:1.2em;font-weight:500}footer-temp{border-top:1px solid #e8e8e8;padding:30px 0px}span.archive-title{font-size:1.1em}h1{font-size:1.5em;font-weight:700}h2{font-size:1.35em}h3,h4,h5{font-size:1.2em}h5{margin-top:1.5em}h6{font-size:1em}h1.entry-title>a{color:black}h1.archive-post>a{color:black}h2#archive-title{font-size:1.5em;color:#A00000}a:hover{text-decoration:none}a.red{color:#A00000}a.url-title{font-size:0.8em}a.prev{margin-right:20px}.categories>a{color:#A00000;font-size:0.85em}.white{color:#FFF}div.entry-content{padding-bottom:60px}ul.main-navigation{text-align:left;padding-bottom:35px}ul.main-navigation>li{padding-right:30px;font-family:"Nanum Gothic Coding"}ul.main-navigation>li:after{content:" /"}ul.main-navigation>li>a{color:black}ul.main-navigation>li>a:hover{color:#A00000}ol.reference{counter-reset:list}ol.reference>li{list-style:none}ol.reference>li:before{content:"[" counter(list) "] ";counter-increment:list}.mB50{margin-bottom:50px}.mB40{margin-bottom:40px}.mT30{margin-top:30px}.m20{margin:20px 0px}.clear{clear:both}.sharing>div.sh_button{float:left;padding:4px}img{margin-top:10px;margin-bottom:10px}img.center{display:block;margin-left:auto;margin-right:auto}html{font-family:"Nanum Gothic Coding"}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Nanum Gothic"}body{font-family:"Nanum Gothic"}code,pre{font-family:"Nanum Gothic Coding"}select optgroup{font-family:"Nanum Gothic"}.footnote{vertical-align:super;font-size:50%}ul{list-style:disc}ul.mL10>li{margin-left:10px}