Alternating Least Squares (ALS) 의 예시와 논문 수식 유도

정준환·2022년 10월 19일
1
post-thumbnail

[논문 리뷰] Collaborative Filtering for Implicit Feedback Datasets 에서 넘어왔습니다.

사용 배경


함수 y=f(x)y = f(x)의 최솟값을 찾는 방법은 여러가지 방법이 있을 수 있다.

만약 f(x)f(x)가 이차함수라면 아무 생각없이 f(k)=0f'(k) = 0 을 만족하는 kk를 찾아서 f(k)f(k)가 최소라고 했을 것이다. (최고차항의 계수가 음수인 경우는 그냥 무시하자...) 이렇게 이차함수와 같이 한번의 미분만으로 최솟값을 찾을 수 있는 형태의 함수들을 Convex 하다고 한다. 하지만 f(x)f(x)가 사차함수만 되어도 이 방법은 사용이 불가능하다. 일반적으로 사차함수는 f(k)=0f'(k) = 0을 만족시키는 kk가 3개 존재하고, 단순히 저 정보만 가지고는 그 중 어떤 kk가 최소가 되게 하는지 알 수 없다.

현실 세계의 데이터는 당연히 사차함수보다 훨씬 더 복잡한 형태를 가지고 있다. 이러한 이유 때문에 Machine learning에서 Loss function의 최솟값을 구하기 위해 위의 방법을 사용할 수 없고 Gradient descent method를 사용하는 것이다.

그런데 만약, 우리가 최적화하고자 하는 함수가 convex하다면, gradient descent method를 쓰지 않고 그냥 미분해서 0이 되는 지점을 찾아 훨씬 더 쉽게 최적화 할 수 있지 않을까? 라는 아이디어에서 출발한다.

예시


예를 들어 보자

z=5x2y2+2x2+3y2100xyz = 5x^2y^2 + 2x^2 + 3 y^2 -100 xy

위의 zz를 최소화 하는 x,yx, y를 찾아보자.

위 식에서 yy가 상수라면 xx에 대한 이차식으로 생각할 수 있다. 따라서 yy가 어떤 값인지는 모르지만, 그 때 최소가 되는 xx는 쉽게 구할 수 있을 것이다.

0=zx=(10y2+4)x100y x=100y10y2+4(1)0 = \frac{\partial z}{\partial x} = (10y^2 + 4) x - 100y \\ \ \\ x = \frac{100 y}{10 y^2 + 4} \qquad \cdots \qquad (1)

yy에 대해서도 동일하다.


0=zy=(10x2+6)y100x y=100x10x2+6(2)0 = \frac{\partial z}{\partial y} = (10x^2 + 6) y - 100x \\ \ \\ y = \frac{100 x}{10 x^2 + 6} \qquad \cdots \qquad (2)

식 (1), (2)를 이용해서 최솟값을 찾아보자. 시점은 그냥 마음대로 (1,1)(1, -1) 에서 시작한다.

먼저 y를 고정하고 식 (1)을 이용해서 xx값을 찾는다.


x=100147.14x = \frac{-100}{14} \approx -7.14

이 값이 의미하는 바는, y=1y=-1 일 때, x7.14x\approx-7.14 에서 zz가 최소화 된다는 뜻이다. 그런데 yy는 당연히 -1이 아니고, 이제 식 (2)를 이용해 주어진 x7.14x \approx -7.14에 대해 yy를 구해보자.


y=100(7.14)10(7.14)2+61.38y = \frac{100 \cdot (-7.14)}{10 \cdot (-7.14)^2 + 6} \approx -1.38

다시 한번, x=7.14x=-7.14 일 때, y1.38y \approx -1.38 에서 최소를 갖는 것이다. 따라서 이 yy의 값을 이용해서 xx를 다시 구하고, 또 yy를 구하고를 반복하면 zz를 최소로 하는 x,yx, y를 구할 수 있을 것이다. 아래 python 코드를 활용하여 해당 과정을 반복해보았다.


def cal_z(x, y):
    return 5 * x**2 * y**2 + 2 * x**2 + 3 * y**2 - 100 * x * y

def cal_x(y):
    return (100 * y) / (10 * (y**2) + 4)

def cal_y(x):
    return (100 * x) / (10 * (x**2) + 6)

cnt = 0
x, y = 1, -1
while cnt < 30:
    print(cnt, x, y, cal_z(x,y))
    x = cal_x(y)
    y = cal_y(x)
    cnt += 1

cntxxyyzz
01-1-392.15
1-7.14-1.38-420.27
2-5.98-1.65-433.44
\vdots\vdots\vdots\vdots
29-3.42-2.78-452.21

cnt = 2929 일 때, zz 값은 452.21-452.21 로 계산되었다.

이러한 방식으로 x,yx, y를 고정시키며 번갈아가며 최적화 하는 방법을 Alternating Least Squares 라고 한다.

Wolfram alpha로 그래프를 그려보니 아래와 같이 나왔고, 대략 (3.41284,2.78657)(-3.41284, -2.78657) 에서 452.21-452.21의 최솟값을 갖는다고 한다.

논문 수식 유도


아래 식에 있는 문자에 대한 의미가 궁금하시면 링크를 참조해주세요.


u,icui(puixuTyi)2+λ(uxu2+iyi2)(3)\sum_{u, i} c_{ui}(p_{ui} - x_u^Ty_i) ^2 + \lambda \left( \sum_u\|x_u\|^2 + \sum_i\|y_i\|^2 \right) \qquad \cdots \qquad (3)

우리는 위의 식 (3)을 최소화 하는 xu,yix_u, y_i들을 찾는것이 목표다. 식을 잘 보면 x,yx, y가 서로 곱해진 상태로 제곱이 되어있기 때문에, 함수의 형태가 convex함을 보장할 수 없다. 그러나 yiy_i가 상수라고 생각하면, xux_u에 대한 이차식이고, xux_u가 상수라고 생각하면 yiy_i에 대한 이차식이 된다. cui,λc_{ui}, \lambda는 항상 양수이기 때문에, 해당 식은 convex하게 된다. 이제 위의 예시에서 본 방법을 이용하여 xu,yix_u, y_i를 구해보자.


XX : xux_u를 쌓아서 만든 m×fm \times f matrix
YY : yiy_i를 쌓아서 만든 n×fn \times f matrix

CuC^u : Ciiu=cuiC^u_{ii} = c_{ui} 인 diagonal n×nn \times n matrix
CiC^i : Cuui=cuiC^i_{uu} = c_{ui} 인 diagonal m×mm \times m matrix

p(u)p(u) : user uu에 대해 puip_{ui}를 나열해놓은 Rn\R^n차원의 벡터
p(i)p(i) : item ii에 대해 puip_{ui}를 나열해놓은 Rm\R^m차원의 벡터

라고 하면,

xu=(YTCuY+λI)1YTCup(u) yi=(XTCiX+λI)1XTCip(i)x_u = (Y^TC^uY + \lambda I)^{-1}Y^TC^up(u) \\ \ \\ y_i = (X^TC^iX + \lambda I)^{-1}X^TC^ip(i)

를 얻을 수 있다. 이제 시작해보자.


먼저 cui(puixuTyi)2c_{ui}(p_{ui} - x_u^Ty_i) ^2 부터 처리해본다.


xuTyi=xu1yi1+xu2yi2++xuf+yif cui(puixuTyi)2=cui(pui(xu1yi1+xu2yi2++xuf+yif))2\begin{aligned} x_u^T y_i &= x_{u1} y_{i1} + x_{u2} y_{i2} + \cdots + x_{uf} + y_{if} \\ \ \\ c_{ui}(p_{ui} - x_u^Ty_i) ^2 &= c_{ui}(p_{ui} - (x_{u1} y_{i1} + x_{u2} y_{i2} + \cdots + x_{uf} + y_{if})) ^2 \end{aligned}

xu=[xu1,xu2,,xuf]Tx_u = [x_{u1}, x_{u2}, \cdots, x_{uf}]^T 임을 유의하자. 따라서 scalar 값인 cui(puixuTyi)2c_{ui}(p_{ui} - x_u^Ty_i) ^2xux_u로 미분하면 마찬가지로 ff차원의 벡터가 나와야 한다.


  xucui(puixuTyi)2=[xu1cui(puixuTyi)2,,xufcui(puixuTyi)2]T=[2cui(puixuTyi)(yi1),,2cui(puixuTyi)(yif)]T=2cui(puixuTyi)yi(4)\begin{aligned} & \ \ \quad \frac{\partial}{\partial x_u} c_{ui}(p_{ui} - x_u^Ty_i) ^2 \\ &=\left[\frac{\partial}{\partial x_{u1}} c_{ui}(p_{ui} - x_u^Ty_i) ^2, \cdots,\frac{\partial}{\partial x_{uf}} c_{ui}(p_{ui} - x_u^Ty_i) ^2\right]^T \\ &=\left[2c_{ui}(p_{ui} - x_u^Ty_i)(-y_{i1}), \cdots , 2c_{ui}(p_{ui} - x_u^Ty_i)(-y_{if}) \right]^T \\ &= -2c_{ui}(p_{ui} - x_u^Ty_i) y_i \tag{4} \end{aligned}

다음은 λ(uxu2+iyi2)\lambda \left( \sum_u\|x_u\|^2 + \sum_i\|y_i\|^2 \right) 을 처리해보자.


xu2=xu12+xu22++xuf2\|x_u\|^2 = x_{u1}^2 + x_{u2}^2 + \cdots + x_{uf}^2

어차피 xux_u에 대해서 미분 할 것이기 때문에 다른 유저나, 아이템들은 상수 취급할 수 있다.


 xuλ(uxu2+iyi2)=2λ[xu1,xu2,,xuf]T=2λxu(5)\begin{aligned} & \ \quad \frac{\partial}{\partial x_u} \lambda \left( \sum_u\|x_u\|^2 + \sum_i\|y_i\|^2 \right) \\ &= 2 \lambda [x_{u1}, x_{u2}, \cdots , x_{uf}]^T \\ &= 2 \lambda x_u \tag{5} \end{aligned}

식 (3)을 식 (4), (5) 를 이용하면 xux_u에 대해 미분할 수 있다.


0=xu(u,icui(puixuTyi)2+λ(uxu2+iyi2))=ixucui(puixuTyi)2+xuλ(uxu2+iyi2)=i2cui(puixuTyi)yi+2λxu\begin{aligned} 0 &= \frac{\partial}{\partial x_u} \left( \sum_{u, i} c_{ui}(p_{ui} - x_u^Ty_i) ^2 + \lambda \left( \sum_u\|x_u\|^2 + \sum_i\|y_i\|^2 \right) \right) \\ &= \sum_{i} \frac{\partial}{\partial x_u} c_{ui}(p_{ui} - x_u^Ty_i) ^2 + \frac{\partial}{\partial x_u} \lambda \left( \sum_u\|x_u\|^2 + \sum_i\|y_i\|^2 \right) \\ &= \sum_{i} -2c_{ui}(p_{ui} - x_u^Ty_i) y_i + 2 \lambda x_u \end{aligned}

1-2 줄 사이에서 xux_u에 대한 미분이므로 u,i\sum_{u, i}에서 uu에 대한 항을 날릴 수 있었다. 식을 조금 정리해보자.


0=i2cui(puixuTyi)yi+2λxuλxu=icui(puixuTyi)yi=i(cuipuiyicuixuTyiyi)\begin{aligned} 0 &= \sum_{i} -2c_{ui}(p_{ui} - x_u^Ty_i) y_i + 2 \lambda x_u \\ \lambda x_u &= \sum_{i} c_{ui}(p_{ui} - x_u^Ty_i) y_i \\ &= \sum_{i}( c_{ui}p_{ui}y_i - c_{ui} x_u^Ty_i y_i) \end{aligned}

xuTyix_u^Ty_i는 scalar 이므로 xuTyi=yiTxux_u^Ty_i = y_i^T x_u가 성립한다. 또한 scalar이므로 곱셈의 순서를 바꿀 수 있다. 이를 이용하면


λxu=i(cuipuiyicuixuTyiyi)=i(cuipuiyicuiyixuTyi)=i(cuipuiyicuiyiyiTxu)=icuipuiyi(icuiyiyiT)xu\begin{aligned} \lambda x_u &= \sum_{i}( c_{ui}p_{ui}y_i - c_{ui} x_u^Ty_i y_i) \\ &= \sum_{i}( c_{ui}p_{ui}y_i - c_{ui} y_i x_u^Ty_i) \\ &= \sum_{i}( c_{ui}p_{ui}y_i - c_{ui} y_i y_i^T x_u) \\ &= \sum_{i} c_{ui}p_{ui}y_i - \left(\sum_{i} c_{ui} y_i y_i^T \right) x_u \\ \end{aligned}
(icuiyiyiT+λI)xu=icuipuiyi(6)\therefore \left(\sum_{i} c_{ui} y_i y_i^T + \lambda I \right) x_u = \sum_{i} c_{ui}p_{ui}y_i \tag{6}

이제 위 방정식을 만족하는 xux_u를 구하면 된다.


icuipuiyi\sum_{i} c_{ui}p_{ui}y_i 를 먼저 보자.


icuipuiyi=cu1pu1y1+cu2pu2y2++cunpunyn(7)\sum_{i} c_{ui}p_{ui}y_i = c_{u1}p_{u1}y_1 + c_{u2}p_{u2}y_2 + \cdots + c_{un}p_{un}y_n \tag{7}

YY : yiy_i를 쌓아서 만든 n×fn \times f matrix
CuC^u : Ciiu=cuiC^u_{ii} = c_{ui} 인 diagonal n×nn \times n matrix
p(u)p(u) : user uu에 대해 puip_{ui}를 나열해놓은 Rn\R^n차원의 벡터

라고 위에서 약속했다. 이해를 위해 수식을 써보면 아래와 같다.


Y=[y1y2yn]=[y11y12y1fy21y22y2fyn1yn2ynf]Y = \begin{bmatrix} y_1 \\ y_2 \\ \vdots \\ y_n \end{bmatrix} = \begin{bmatrix} y_{11} && y_{12} && \cdots &&y_{1f}\\ y_{21} && y_{22} && \cdots &&y_{2f}\\ \vdots &&\vdots &&\ddots && \vdots\\ y_{n1} && y_{n2} && \cdots &&y_{nf}\\ \end{bmatrix}

Cu=[cu1000cu2000cun]C^u = \begin{bmatrix} c_{u1} & 0 & \cdots & 0 \\ 0 & c_{u2} & \cdots & 0 \\ \vdots & \vdots & \ddots& \vdots \\ 0 & 0 & \cdots & c_{un} \end{bmatrix}

p(u)=[pu1pu2pun]Tp(u) = \begin{bmatrix} p_{u1} & p_{u2} & \cdots& p_{un} \end{bmatrix} ^ T

이를 이용해서 식 (7)을 다시 쓸 수 있다.

먼저 Cup(u)C^up(u)를 계산하면 다음과 같은 n×1n \times 1 행렬을 얻을 수 있다.


Cup(u)=[cu1pu1cu2pu2cunpun]C^up(u) = \begin{bmatrix} c_{u1}p_{u1}\\ c_{u2}p_{u2}\\ \vdots\\ c_{un}p_{un} \end{bmatrix}

이를 보면 해당 행렬이 식(7)의 yiy_i의 계수와 동일함을 확인할 수 있다.

이제 여기에 YTY^T를 곱해보자. 그럼 아래와 같은 f×1f \times 1 행렬이 나온다.


YTCup(u)=[y1y2yn][cu1pu1cu2pu2cunpun]=[cu1pu1y1+cu2pu2y2++cunpunyn]\begin{aligned} Y^TC^up(u) &= \begin{bmatrix} y_1 & y_2 & \cdots & y_n \end{bmatrix} \begin{bmatrix} c_{u1}p_{u1}\\ c_{u2}p_{u2}\\ \vdots\\ c_{un}p_{un} \end{bmatrix} \\ &= \begin{bmatrix} c_{u1}p_{u1}y_1 + c_{u2}p_{u2}y_2 + \cdots + c_{un}p_{un} y_n \end{bmatrix} \end{aligned}
 icuipuiyi=YTCup(u)(8)\therefore \ \sum_{i} c_{ui}p_{ui}y_i = Y^TC^up(u) \tag{8}

마지막으로 icuiyiyiT\sum_{i} c_{ui} y_i y_i^T 를 풀어보자. 위와 동일한 Y,Cu,p(u)Y, C^u, p(u)를 사용한다.


icuiyiyiT=cu1y1y1T+cu2y2y2T++cunynynT\sum_{i} c_{ui} y_i y_i^T = c_{u1}y_1y_1^T + c_{u2}y_2y_2^T + \cdots + c_{un} y_n y_n^T \\

YTCuY^TC^u 를 계산해보자. 아래와 같은 f×nf \times n 행렬을 얻을 수 있다.


YTCu=[y1y2yn][cu1000cu2000cun]=[cu1y1cu2y2cunyn]\begin{aligned} Y^TC^u &= \begin{bmatrix} y_1 & y_2 & \cdots & y_n \end{bmatrix} \begin{bmatrix} c_{u1} & 0 & \cdots & 0 \\ 0 & c_{u2} & \cdots & 0 \\ \vdots & & \ddots& \vdots \\ 0 & 0 & \cdots & c_{un} \end{bmatrix} \\ &= \begin{bmatrix} c_{u1}y_1 & c_{u2}y_2 & \cdots & c_{un}y_n \end{bmatrix} \end{aligned}

이제 여기에 YY를 곱해보면 아래와 같다.


YTCuY=[cu1y1cu2y2cunyn][y1y2yn]=[cu1y1y1T+cu2y2y2T++cunynynT]\begin{aligned} Y^TC^u Y &= \begin{bmatrix} c_{u1}y_1 & c_{u2}y_2 & \cdots & c_{un}y_n \end{bmatrix} \begin{bmatrix} y_1 \\ y_2 \\ \vdots \\ y_n \end{bmatrix} \\ &= \begin{bmatrix} c_{u1}y_1y_1^T + c_{u2}y_2y_2^T + \cdots + c_{un} y_n y_n^T \end{bmatrix} \\ \end{aligned}
icuiyiyiT=YTCuY(9)\therefore \sum_{i} c_{ui} y_i y_i^T = Y^TC^uY \tag{9}

식 (6), (8), (9)를 이용하면,


(icuiyiyiT+λI)xu=icuipuiyi(YTCuY+λI)xu=YTCup(u)\begin{aligned} \left(\sum_{i} c_{ui} y_i y_i^T + \lambda I \right) x_u &= \sum_{i} c_{ui}p_{ui}y_i \\ (Y^TC^uY + \lambda I) x_u &= Y^TC^up(u) \\ \end{aligned}
xu=(YTCuY+λI)1YTCup(u)\therefore x_u = (Y^TC^uY + \lambda I)^{-1}Y^TC^up(u)

마찬가지 방법을 사용하면 yiy_i에 대해서도 계산할 수 있다.

꽤 복잡한 것 같지만 한줄 씩 천천히 읽어보면 충분히 해결할 수 있을 것이다.

profile
정준환

0개의 댓글