권장 시스템 시리즈의 (a) : FM 이론 및 실습

권장 지역에서는 종종 범주 적 특성을 결합 할 수 있습니다. 그러나 일반적으로 조합의 두 가지 문제가 있습니다 :

  • 하나의 핫 기능 희소성의 높은 수준, 차원의 저주 후.
  • 일반 선형 모델은 기능 사이의 관계를 고려하지 않습니다. \ (Y = w_0 + \ sum_ {I = 1} ^ {N} w_ix_i \)

FM (인수 분해 기계) 기능의 조합의 과정에서 발생하는 이러한 문제점은 효율적인 솔루션을 제안입니다. [1]

1. 특성 관계

먼저, 상기 제 문제를 해결하는 일반적인 선형 모델의 특성 사이의 관계는 2 차 다항식 모델에 다시 설정 될 수있다. 여기에만 높은 차 다항식을 설명하지 않습니다 2 차에 대해 설명합니다.
\ [Y = w_0 + \ sum_ {I = 1} ^ nw_ix_i + \ sum_ {I = 1} ^ {N-1} \ sum_ {J = I + 1} ^ nw_ {IJ} x_ix_j의 \ 태그 {1} \]

2. 매개 변수를 정량화하기

상기 식으로, 당신은 고려할 필요가 매개 변수를 해결하는 방법입니다. 한 후 핫 특징 다수 희소성 고도에 문제가 있기 때문에, 상기 식 (\ x_ix_j의 \)은 또한 제로 값을 다수 생성한다. 매개 변수 학습은 직접적인 결과는 충분하지 않습니다 \ (W_ {IJ} \) 훈련을 통해 얻을 수 없습니다. 설명 : 주문 \ (X-x_ix_j = \) 다음 \ (\ FRAC {\ 부분 {Y}} {\ 부분 W_ {{}}} X- = \의 IJ) 때문에 \ (X-0 = \) 이므로 \ (W_} ^ {새로운 새 W_의 {IJ} = {} ^ {이전의 IJ} + {\ X 알파 = W_} 이전의} ^ {(IJ)가} {\) , 구배 0 파라미터를 업데이트 할 수 없다.

이 상황의 근본 원인이다 너무 드문 드문 있습니다. 우리는 것을, 방법을 찾아 낼 것으로 예상 \ (W_ {IJ가} \) 희소성 해결 기능에 영향을받지 않습니다.

함으로써 매트릭스 분해는 각 기능에 대해, 고무 (x_i로부터 \) \ 보조 벡터 (암시 벡터)에 도입 \ (V_I = (V_ {I1} V_ {I2} \ cdots는 V_는 {IK}) \) 다음 사용 \ (V_iV_j ^ T \)\ (IJ W_ {}는 \) 해결된다. 즉, 다음과 같은 가정입니다 : \ ({W_의 IJ} \ 약 V_iV_j ^ T \) .

인 벡터의 도입의 숨겨진 장점 :

  1. 2 차 기간의 최초 양의 정수 \는 (\ FRAC {N (N -1)} {2} \) 감소 \ (KN \) .

  2. 이 원래의 매개 변수 사이의 관계는 없지만, 지금은 암시 적 벡터를 통해 관계를 구축 할 수 있습니다. 이전과 \ (W_ {IJ} \)\ (W_ {IK} \) 독립적 인,하지만 지금은 $ W_ {(IJ)} = \ langle V_I, V_j \ rangle, W_ {IK} = \ langle V_I, V_k \ rangle 이 두 가지의 일반적인 $ \ (V_I \) , 즉, 모두를 포함 \ (x_ix_j \) 특징의 결합 제로 (a 존재 \ (J \ NEQ I \) 이므로 \ (x_ix_j \ NEQ 0 \) ) 샘플을 암시 벡터 연구하는 데 사용할 수 있습니다 \ (V_I \) 주로 데이터 희소성으로 인한 영향을 피할. [2]

지금 식 (1) 재기록 가능 :
\ [Y = W 0 + \ sum_ {I = 1} ^ nw_ix_i + \ sum_ {I = 1} ^ {N - 1} \ sum_ {J = I + 1} ^ N- \ langle V_I, v_j \ rangle x_ix_j \ 태그
{2} \] 방정식을 해결하는 방법으로 포커스 (2) 2 차 조건 뒤에.

우선 이해 대칭 상 삼각 행렬 설정 행렬 합산 \ (M를 \) : \
{1N} m_Low m_Low \\ 11 m_Low} {{매트릭스} 시작 \ ([M = \ 왼쪽 및 12 m_Low {} \ & cdots이다. {21} {22} M_ 및 \ cdots 및 1N M_ {} \\ \ vdots 및 \ vdots 및 \ ddots 및 \ vdots \\ M_ {} N1 N2 M_ {} \ cdots 및 NN M_ {} \\ \ {단부 매트릭스} \ 오른쪽)
_ {n 개의 *의 N} \] , \ (m_Low} = m_Low JI의 IJ {{} \) .

캠 요소가되도록 (A \) \ , 즉 \ (\ sum_ {I =. (1)} ^ {N--. 1} \ sum_ {J는 = I는 +. (1)} ^ {N-} m_Low {의 IJ}는 \를 =)를 . 그리고, \ (M \) 동등한 모든 요소 \ (A + TR * 2 (M) \) \ (TR (M) \) 행렬의 트레이스이다.
\ [\ Sum_ {I = 1 } ^ N 개의 \의 sum_ {J = 1} ^ nm_ {IJ} = 2 * \ sum_ {I = 1} ^ {N-1} \ sum_ {J = I + 1} ^ { N} M_ {IJ} + \
sum_ {I = 1} ^ {N} M_ {II} \] 가능한
\ [A = \ sum_ {I =. (1)} ^ {N--. 1} \ sum_ {J = I +1} ^ {N} M_ { IJ} = \ FRAC {1} {2} * \ 좌측 \ {\ sum_ {I = 1} ^ N 개의 \의 sum_ {J = 1} ^ nm_ {IJ} - \ sum_ { I = 1} ^ {N}
M_ II {} \ 오른쪽 \} \] 전제로, 유도 될 수 식 (2) 2 차 조건 :
\ [\ 시작 {정렬} \의 sum_ {I = 1} ^ {N-1} \ sum_ {J = I + 1} ^ N 개의 \의 langle V_I, V_j \ rangle x_ix_j \ notag \\ = {} \ FRAC {1} {2} * \ 좌측 \ {\ sum_ {I = 1} ^ {N} \ sum_ {J = 1} ^ {N} \의 langle V_I, V_j \ rangle x_ix_j- \ sum_ {I = 1} ^ {N} \의 langle V_I, V_I \ rangle x_ix_i 오른쪽 \ \} \ notag \\ = {} \ FRAC {1} {2} * \ 좌측 \ {\ sum_ {I = 1} ^ {N} \ sum_ { J = 1} ^ {N} \ sum_ {F = 1} ^ {K} V_ {경우} V_ {JF} x_ix_j- \ sum_ {I = 1} ^ {N} \ sum_ {F = 1} ^ {K } V_ {경우} V_ {경우} x_ix_i \ 오른쪽 \} \ notag \\ = {} \의 FRAC {1} {2} * \ sum_ {F = 1} ^ {K} \ 좌측 \ {\ sum_ {I = 1} ^ {N} \ {sum_ J = 1} ^ {N} {V_ 경우} {x_iv_ JF x_j-} \ {sum_ I = 1} ^ {N}} {V_ 경우 ^ {2} {I X_ } ^ 2 \ 오른쪽 \} \ notag \\ = {} \ FRAC {1} {2} * \ sum_ F = {1} ^ {K} \ 좌측 \ {\ 좌측 (\ sum_ I = {1} ^ {N} V_ {경우} x_i로부터 \ 오른쪽) \ 좌측 (\ sum_ {J = 1} ^ {N} V_ {JF} x_j \ 오른쪽) - \ sum_ {I = 1} ^ {N} V_ {경우} ^ {2} X_ {I} 오른쪽 \} \ notag \\ = {} \의 FRAC {1} {2} * \ sum_ {F = 1} ^ {K} \ 좌측 \ {\ 왼쪽 ^ 2 \ (\ sum_ {I = 1} ^ {N} V_ {경우} x_i로부터 \ 오른쪽) ^ {2} - 우측 \ sum_ {I = 1} ^ {N} V_ {경우} ^ {2} X_ {I} ^ 2 \ \ } \ notag \\ \ 단부 정렬 {} \ 태그 {3} \]
(2) (3)을 얻을 수있다 바인딩 :
\ [\ 선두 Y = {} & W 0 + \ sum_ {. I = 1} ^ nw_ix_i + \ sum_ {I = 1.} {= 왼쪽 정렬} ^ {N-- 1.} \ sum_ {J = I + 1} ^ N 개의 \의 langle V_I, v_j \ rangle x_ix_j \ notag \\ = {} w_0 + \ sum_ {I = 1} ^ nw_ix_i + \ FRAC {1} {2} * \ sum_ {F = 1} ^ {K} \ 좌측 \ {\ 좌측 (\ sum_ {I = 1} ^ {N} V_ {} 경우 x_i로부터 \ 오른쪽) ^ {2} - \ sum_ {I = 1} ^ {N} V_ {} 경우 ^ {2} X_ {난} ^
2 \ 바로 \} \ notag \\ \ 끝 {정렬} \ 태그 {4} \] 이 시점에서, 우리는 모델 식을 싶어.

이유는 식 (2) 식으로 재 기입한다 (4)의 계산 전에 재기록 때문에 \ (Y \) 의 복잡성은 \ (O (KN ^ 2) \) , 계산 복잡도가 재 작성된다 (\ O (KN) \) , 모델 추론 속도를 향상시킬 수 있습니다.

어떻게

지금까지 우리는 단지 모델이 나왔다 정의, 어떻게 그것을 해결하기 위해? 매개 변수는 기울기 하강, 매개 변수 다음 그라데이션 모델 계산 표현의 가장 일반적인 방법을 해결하는 데 사용할 수 있습니다 :

파라미터이면 \ (W 0 \) , \ (\ FRAC는 {\ {Y 부분 {}} \ 부분}} = {0.1 \ W) .

매개 변수 인 경우 \ (W_i \) 하면 \ (\ {FRAC \ {Y 부분 {}} \ 부분}} = {W_i x_i로부터 \) .

매개 변수 인 경우 (V_ {경우} \) \ , 고차 조건 만 계산 매개 변수 모델에 초점을 맞출 필요가 (V_ {경우} \) \ 남아있는 독립 파라미터의 기울기는 상수로 간주 될 수있다.
\ [\ \ {정렬} 시작 FRAC을 {\ 부분 {Y}} {\ 부분 {V_ {}} 경우} = {} \ 부분 {\ FRAC {1} {2} \ 좌측 \ {\ 좌측 (\ sum_ {I = 1} ^ {N } V_ {경우} x_i로부터 \ 오른쪽) ^ {2} - 우측 \ sum_ {I = 1} ^ {N} V_ {경우} ^ {2} X_ {I} ^ 2 \ \ }} / \ 부분 V_ {{ }} 경우 \ notag \\ = {} \ FRAC {1} {2} * \ 좌측 \ {\ {FRAC \ 부분 {\ 좌측 \ {\ sum_ I = {1} ^ {N} V_ {경우} x_i로부터 \ 오른쪽 \} ^ 2}} {\ 부분 {V_ {}}} 경우 - \의 FRAC {\ 부분 {\ 좌측 \ {\ sum_ {I = 1} ^ {N} V_ { 경우} ^ {2} X_ {
I} ^ 2 \ 오른쪽 \}}} {\ 부분 {V_ {} \ 오른쪽 \}}} 경우 \ notag \\ \ 단부 {정렬} \ 태그 {5} \] 에있어서,
\ [\ FRAC {\ 부분 { \ 좌측 \ {\ sum_ {I = 1} ^ {N} V_ {} 경우 ^ {2} X_ {I} ^ 2 \ 오른쪽 \}}} {\ 부분 {V_ {만약 }}} = 2x_ {I}
^ 2v_ {경우} \ 태그 {6} \] 오더 \ (\ 람다 = \ sum_ 1 = {I}} ^ {N-V_} {IF x_i로부터 \.) 다음 :
\ [\ \ {정렬} 시작 FRAC을 {\ 부분 {\ 좌측 \ {\ sum_ {I = 1} ^ {N} V_ {} 경우 x_i로부터 \ 오른쪽 \} ^ 2}} {\ 부분 {V_ {경우}} } = {} \의 FRAC { \ 부분 {\ 람다 ^ 2}} {\ 부분 {V_ {} 인 경우}} \ notag \\ = {} \의 FRAC {\ 부분 {\ 람다 ^ 2}} {\ 부분 {\ 람다}} \ {FRAC \ 부분 {\ 람다 {}} \ {V_ 부분 {} 인 경우}} \ notag \\ = {2} \ 람다 * \ {FRAC \ 부분 {\ sum_ I = {1} ^ {N} {V_ 경우} x_i로부터 {}} \ {부분 V_ 경우 {}}} \ notag \\ = {2} \ x_i로부터 람다 * \ notag \\ = {} * 2 x_i로부터 * \ {J sum_ = 1} ^ {N} V_
{} JF x_j \ notag \\ \ 단부 정렬 {} \ 태그 {7} \] 식 (5-7)을 얻을 수있다 결합 :
\ [\ {FRAC \ 부분} {Y} {\ 부분 {V_ {} 경우
}} = \ ^ V_ \ \] x_i로부터 sum_ {J = 1} {N} {JF} x_j-X_ {I} ^ 2v_ {경우} 태그 {8} 요약하면, 최종 모델 다음과 같은 구배 발현 파라미터는 :
\ [\ {식} \ FRAC {\ 부분 {Y}} {\ 부분 {\ 세타가}} = \ 시작 {케이스} 1, \ 텍스트 w_0 \ 세타 \ 텍스트는 {} {경우}를 시작한다; \\ x_i로부터, \ 텍스트 \ 세타 \ 텍스트 {} 인 경우 {w_i}; \\ x_i로부터 \ sum_ {J = 1} ^ {N} V_ {JF} x_j-X_ {I} ^ 2v_ {경우}, \ 텍스트 {경우} \ 세타 \ 텍스트는 {} V_ {경우}. \ {단부 경우} \ {식 단부} \ notag \]

1. 성능 분석

위의 절에 대한 시간 복잡도 FM 예보에서 \ (O (KN) \) . 그래디언트 파라미터 표현에 기초하여 트레이닝 복잡도 분석, \ (\ sum_. 1} = {J} ^ {N-V_ JF} {X_ {J} \)\ (나는 \) 에 관계없이 제 파라미터의 갱신 될 수있다 모든 \ (\ sum_ {J = 1 } ^ {N} V_ {JF} X_ {J는} \) , 복잡도 계산 \을 (O (KN) \) , 양자의 시간 복잡도 후속 업데이트가 모든 파라미터 이다 \ (O. (1) \) 의 매개 변수의 양 \. (1 + N- + KN \) 하므로 최종 트레이닝 시간 복잡도 마찬가지로 \ (O (KN) \) 여기서 \은 (\ N-) 기능의 개수 \ (케이 \) 숨겨진 벡터의 차원의 수.

FM 교육 및 예측의 시간 복잡도는 \ (O (KN) \) 이며, 매우 효율적인 모델.

장점 [1] :

총, 우리의 제안 FM의 장점은 다음과 같습니다 :

1)는 FM에 실패 SVM을 매우 희박한 데이터에서 파라미터 추정을 허용한다.

2) FM에 선형 복잡성, 원초적으로 최적화 할 수 있으며, SVM을 같은 지원 벡터에 의존하지 않는있다. 우리는 FM에 훈련 인스턴스의 100 개 수백만 넷플릭스와 같은 대규모 데이터 세트로 확장 것으로 나타났다.

3) FM에 어떤 실제 가치를 특징 벡터로 작업 할 수 있습니다 일반적인 예측이다. 이에 대하여, 다른 최첨단 인수 분해 모델은 매우 제한된 입력 데이터 만 작동한다. 우리는 입력 데이터의 특징 벡터를 정의하여, FM에 치우친 MF, SVD ++, PITF 또는 FPMC 같은 최첨단 모델을 모방 할 수 있음을 보여줍니다.

단점 [6] :

  1. 단지 2 차 크로스 오버 기능이 될 수있다, 여전히 우리는 특징이 작동을 통과해야합니다.

2. 이론 및 실습

FM 적용 할 수 모두 회귀 작업도 분류 작업에 적용 할 수 있습니다. (2) 최 넣어 만 식 번째 분류 작업 같이 \ (S 상 \) , 분석 작업을 회귀 함수에 기초하여 수행된다 구했다.

동일 모델의 최종 손실 함수를 사용할 수 있습니다 같은 회귀 작업 같은 다수의 형태를 취할 수있다 \이 (MSE \) , 분류 작업을 사용할 수 있습니다 \ (크로스 엔트로피 \)를 등등합니다.

이 알려져 있지만 그것은 보조 벡터를 도입하여 계산 될 수 있지만, 기능과 어떻게 보조 벡터 \ (x_i로부터 \)은 즉, 연락처를 설정하는 방법 \ (x_i로부터 \) 를 이용한 벡터 \ (V_I \) ? FM 상기 신경망 사용하여 \ (x_i로부터 \) \을 (매립 그 \) 보조 벡터, 결과로서 \을 (\ 것을 매립) 벡터 세트로 대응 저 차원 밀도 특성을 특징으로 간주 될 수있다 다른 다운 스트림 작업에 응용 프로그램입니다.

본원에 사용 된 바와 같이, \ (100K MovieLens 데이터 집합 \) [3] 사용자 번호, 필름 번호, 동영상 기록을위한 사용자 선호도있는 실험 입력 특징 세트로서 \ (라벨 \) .

특정 코드는 다음을 달성하기 위해 :

# -*- coding:utf-8 -*-
import pandas as pd
import numpy as np
from scipy.sparse import csr
from itertools import count
from collections import defaultdict
import tensorflow as tf


def vectorize_dic(dic, label2index=None, hold_num=None):
  
    if label2index == None:
        d = count(0)
        label2index = defaultdict(lambda: next(d))  # 数值映射表

    sample_num = len(list(dic.values())[0])  # 样本数
    feat_num = len(list(dic.keys()))  # 特征数
    total_value_num = sample_num * feat_num

    col_ix = np.empty(total_value_num, dtype=int)

    i = 0
    for k, lis in dic.items():
        col_ix[i::feat_num] = [label2index[str(k) + str(el)] for el in lis]
        i += 1

    row_ix = np.repeat(np.arange(sample_num), feat_num)
    data = np.ones(total_value_num)

    if hold_num is None:
        hold_num = len(label2index)

    left_data_index = np.where(col_ix < hold_num)  # 为了剔除不在train set中出现的test set数据

    return csr.csr_matrix(
        (data[left_data_index], (row_ix[left_data_index], col_ix[left_data_index])),
        shape=(sample_num, hold_num)), label2index

def batcher(X_, y_, batch_size=-1):

    assert X_.shape[0] == len(y_)

    n_samples = X_.shape[0]
    if batch_size == -1:
        batch_size = n_samples
    if batch_size < 1:
        raise ValueError('Parameter batch_size={} is unsupported'.format(batch_size))

    for i in range(0, n_samples, batch_size):
        upper_bound = min(i + batch_size, n_samples)
        ret_x = X_[i:upper_bound]
        ret_y = y_[i:upper_bound]
        yield(ret_x, ret_y)

def load_dataset():
    cols = ['user', 'item', 'rating', 'timestamp']
    train = pd.read_csv('data/ua.base', delimiter='\t', names=cols)
    test = pd.read_csv('data/ua.test', delimiter='\t', names=cols)

    x_train, label2index = vectorize_dic({'users': train.user.values, 'items': train.item.values})
    x_test, label2index = vectorize_dic({'users': test.user.values, 'items': test.item.values}, label2index, x_train.shape[1])

    y_train = train.rating.values
    y_test = test.rating.values

    x_train = x_train.todense()
    x_test = x_test.todense()

    return x_train, x_test, y_train, y_test

x_train, x_test, y_train, y_test = load_dataset()

print("x_train shape: ", x_train.shape)
print("x_test shape: ", x_test.shape)
print("y_train shape: ", y_train.shape)
print("y_test shape: ", y_test.shape)

vec_dim = 10
batch_size = 1000
epochs = 10
learning_rate = 0.001
sample_num, feat_num = x_train.shape

x = tf.placeholder(tf.float32, shape=[None, feat_num], name="input_x")
y = tf.placeholder(tf.float32, shape=[None,1], name="ground_truth")

w0 = tf.get_variable(name="bias", shape=(1), dtype=tf.float32)
W = tf.get_variable(name="linear_w", shape=(feat_num), dtype=tf.float32)
V = tf.get_variable(name="interaction_w", shape=(feat_num, vec_dim), dtype=tf.float32)

linear_part = w0 + tf.reduce_sum(tf.multiply(x, W), axis=1, keep_dims=True)
interaction_part = 0.5 * tf.reduce_sum(tf.square(tf.matmul(x, V)) - tf.matmul(tf.square(x), tf.square(V)), axis=1, keep_dims=True)
y_hat = linear_part + interaction_part
loss = tf.reduce_mean(tf.square(y - y_hat))
train_op = tf.train.AdamOptimizer(learning_rate).minimize(loss)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for e in range(epochs):
        step = 0
        print("epoch:{}".format(e))
        for batch_x, batch_y in batcher(x_train, y_train, batch_size):
            sess.run(train_op, feed_dict={x:batch_x, y:batch_y.reshape(-1, 1)})
            step += 1
            if step % 10 == 0:
                for val_x, val_y in batcher(x_test, y_test):
                    train_loss = sess.run(loss, feed_dict={x:batch_x, y:batch_y.reshape(-1, 1)})
                    val_loss = sess.run(loss, feed_dict={x:val_x, y:val_y.reshape(-1, 1)})
                    print("batch train_mse={}, val_mse={}".format(train_loss, val_loss))

    for val_x, val_y in batcher(x_test, y_test):
        val_loss = sess.run(loss, feed_dict={x: val_x, y: val_y.reshape(-1, 1)})
        print("test set rmse = {}".format(np.sqrt(val_loss)))

결과 :

epoch:0
batch train_mse=19.54930305480957, val_mse=19.687997817993164
batch train_mse=16.957233428955078, val_mse=19.531404495239258
batch train_mse=18.544944763183594, val_mse=19.376962661743164
batch train_mse=18.870519638061523, val_mse=19.222412109375
batch train_mse=18.769777297973633, val_mse=19.070764541625977
batch train_mse=19.383392333984375, val_mse=18.915040969848633
batch train_mse=17.26403045654297, val_mse=18.75937843322754
batch train_mse=17.652183532714844, val_mse=18.6033935546875
batch train_mse=18.331804275512695, val_mse=18.447608947753906
......
epoch:9
batch train_mse=1.394300103187561, val_mse=1.4516444206237793
batch train_mse=1.2031371593475342, val_mse=1.4285767078399658
batch train_mse=1.1761484146118164, val_mse=1.4077649116516113
batch train_mse=1.134848952293396, val_mse=1.3872103691101074
batch train_mse=1.2191411256790161, val_mse=1.3692644834518433
batch train_mse=1.572729468345642, val_mse=1.3509554862976074
batch train_mse=1.3323310613632202, val_mse=1.3339732885360718
batch train_mse=1.1601723432540894, val_mse=1.3183823823928833
batch train_mse=1.2751621007919312, val_mse=1.3023829460144043
test set rmse = 1.1405380964279175

참고

추천

출처www.cnblogs.com/yinzm/p/11619829.html