Matrix Factorization

Matrix Factorization

①linearNetwork Hypothesis

機器學習的作用就是要從一堆數據中學習到學習到某種能力,然後用這種skill來預測未來的結果。比如一個電影推薦的例子,我們手上有很多的電影數據,現在就需要訓練一個機器學習的模型來使得這個模型可以預測一個新來的用戶會喜歡什麼電影,然後推薦過去。或者是對用戶沒有看過的電影進行評分預測。

10624272-3adeca66c747451a.png

Nefix公司曾經舉辦過一場比賽,包含了大量的電影數據信息和近一億個排名信息。用
那麼,問題來了,第幾個用戶的這個n是沒有什麼實際意義的,也就是一個抽象特徵,意味着只是一個編號,隨便給只要不重複即可。但是輸出方面就是很正常了:
表示的就是第n個用戶對第m部電影的排名預測。
10624272-fc429322951e952f.png

仔細看一下這些ID特徵,通常就是數字表示,比如1126,5566,6211等。這些數字編號並沒有什麼太大的意義,都只是一種ID編號而已。這類特徵,被稱爲類別特徵,比如:ID號,blood type,programming languages等等,而許多機器學習模型都是數值特徵,比如linear model,都是一串的數據,決策樹例外,可以是類別區分。所以要建立一個推薦系統的機器學習模型,就要把用戶的ID號這些categorical features轉換成numerical features,這種轉換其實就是一個編碼的過程了。
10624272-f96672113ff9e16f.png

一種比較簡單的就是binary vector encode。也就是說,如果輸入樣本有N個,就構造一個維度爲N的向量。對應第n個樣本那麼第n個位置就是1其他都是0,比如下面的一個例子:
10624272-33f7fe9211d187c1.png

有點像one-hot向量,但不是。編碼之後,那麼用戶的ID就是binary vector了。要注意的是用戶是不一定對每一個電影都會進行評分,可以就是評了一部分而已:
10624272-6813a6d7183c954d.png

我們就是要預測那些沒有被評分的電影。對於這個過程,我們要做的就是掌握每一個用戶對於不同電影的喜愛程度,掌握這個電影的各種特徵factor,比如有多少的喜劇,有多少的懸疑等等。這其實就是一個特徵提取的過程。這裏使用的是
的網絡,N就是輸入的個數,d就是隱藏層的個數,也就是提取出特徵的個數,M就是最後輸出類別的個數。這個結構和之前我們所看的autoencode非常像,但是不同的就是autoencode是最後輸出儘可能的要擬合輸入。
10624272-e235782da16b348a.png

中間還有一個小問題,中間有一個非線性的函數,目的就是要使得整個模型nonlinear化,可以處理非線性的數據,但是在這裏需不需要呢?其實是不需要的,因爲輸入的向量是encoding得到的,大部分是0,小部分是1,那麼就意味着這後面乘上的W權值其實就是隻用一行有用,其他都是0,相當於只有一個權重值進入到tanh函數進行運算。從效果上來說,tanh(x)與x是無差別的,只是單純經過一個函數的計算,並不影響最終的結果,修改權重值即可得到同樣的效果。因爲進入計算之後,修改權值就可以達到效果了,而之前需要的原因是,之前的結果都是需要多個權值就行組合,不加tanh就是線性組合了,所以加來變成非線性。這裏就只有一個,不存在什麼組合,所以直接使用即可。
改進一下,就是下面的圖像了:

10624272-7ae48f791b08fabd.png

對於這種結構,自然就是linearNetwork了,這個網絡結構裏面:就是Nxd,用V來表示,其實應該是V的轉置,隱藏層到輸出層:是dxM,所以進行線性模型之後:
如果是單個用戶,那麼其他的都是0,只有第n個位置纔是1,所以,輸出的hypothesis:

10624272-1c2384a396e8ee7c.png

②Basic Matrix Factorization

上面的變換VX我們看做是一種特徵轉換,φ(x),那麼就可以變成這樣:
如果是對於單部電影:

10624272-0603dca4754dc5d7.png

我們需要做的就是看看排名WV和y的結果要差不多,也就是做擬合,所以error function就是square error function:
10624272-19f4b31b30990a38.png

上式中,灰色的部分是常數,並不影響最小化求解,所以可以忽略。接下來,我們就要求出Ein最小化時對應的V和W解。
根據上式的分解:
矩陣r是R的一個元素,R就是不同電影的排名情況,這種方法叫做Matrix Factorization。
10624272-9919f2b2f2bfca4c.png

所以一個電影的評分是可以分成兩個部分的,一個是V的部分,一個就是W的部分,V可以看做是用戶的部分,W是電影的部分,抽象一下其實就是:V這一行矩陣裏面其實就是各種用戶的feature,也就是分解出來的factorization,比如這個用戶有多喜歡喜劇呀,有多喜歡打戲呀,有多喜歡劇情等等,當然這只是抽象化了而已,畢竟numerical features轉成類別特徵是需要想象力的,比如男生女生轉成0和1,但是0和1想成男生女生就有點難了。對應回上面的,那麼W矩陣自然就是有多少喜劇內容,有多少打戲,有多少劇情了。
10624272-464e0d132a0f3c31.png

最小化Ein函數:
10624272-cbd8b5cd34c7ac19.png

這裏包含了兩組優化的參數,一個是W,一個是V,這種情況有點像SVM的SMO算法,繼續沿用它的想法,固定一個W選擇更新V。
固定W的時候,對每一個用戶做linear regression即可。
固定V的時候,對每一部做linear regression即可。VW在結構上對稱的,所以這兩個東西的優化式子是差不多的,調換一下位置即可。
10624272-54f57313dfe0aabd.png

所以這樣就得到了算法流程:
10624272-3a52eaabe75d131c.png

對於alternating least squares algorithm有兩點要注意的:
①intintialize不能選擇初始化爲0的,因爲矩陣是相乘得到,如果是0,那麼全部都是0了,優化也是0,沒有任何作用。
②converge,收斂性,對於每一步的優化都是衝着減小Ein去的,這就保證了這個算法的收斂性。

10624272-ea8b15b54011a89c.png

④對於Matrix Factorization和Linear Autoencode的比較

10624272-71174b206e0e4399.png

可以看出這兩者是有很強的相似性的,所以linear antoencode可以看做是matrix fatorization的一種形式的。

⑤SGD做優化

之前的迭代是所有的一起,SGD就是隨機梯度下降,隨機找一筆資料,只在這個資料上下做優化,效率很高,程序簡單也容易實現,同時擴展到其他的錯誤也是很簡單的。


10624272-90aa25ca801c52b9.png

對於每一筆資料如上圖


10624272-9369d6e74501fa1b.png

10624272-3b24a13fbe5efdc2.png

如果這個用戶沒有評價過這部電影,那麼就是0,優化也會是0的,這就是爲什麼initial不能爲0的原因。從上述的圖片也可以看出W和V是對稱的,第一項都是residual。所以當我們使用了SGD之後,那麼這個算法的流程就改變了:
10624272-aef069adad1ae73c.png

還有一個需要知道的,在推薦的過程中,用戶的習慣是可能發生改變的,比如三年前喜歡DC,三年後就喜歡漫威了,所以隨着時間的變化,數據也應該隨着時間的變化,所以在使用SGD的過程中,最後的T次迭代我們可以使用時間比較靠近的樣本放入optimization中作爲優化數據,相對來說結果也會比較準。


10624272-4e7b0b02e4f3e515.png

⑥summary

總結一下所學過的提取特徵:


10624272-3d0a50891219433a.png

Adaboost是通過每一次選擇最好的特徵進行劃分,也是一種Extraction Model,Network肯定是了,隱藏層就是一個提取的方法,k近鄰是用距離作爲特徵提取的工具,MF就是剛剛說的矩陣分解了。


10624272-e5d45deb02d534e1.png

以上就是對應的技巧了。

優缺點

優點:簡單,機器可以自動化的提取特徵。powerful,可以處理各種複雜的問題,比如神經網絡。
缺點:hard:比較難,有時候會遇到non-convex的問題,容易得到局部最優。overfit:過擬合的問題,其實上面的矩陣分解還是需要正則化處理的。後面的代碼實現加上了。

10624272-ac0d55916b51013a.png

⑦代碼實現

1.數據的獲取

我們處理的數據是一個矩陣,先找到一些movie的數據:


10624272-11fad311b4805963.png

電影的ID名字,事實上名字我們倒不是特別關心。
還有一個評分的csv:


10624272-9c24506ffdaee4ee.png

我們要看的其實就是123列而已了。我們要做的就是合成一個矩陣:

def load_Data(moves_name, ratings_name):
    print('loading data ......')
    movies = pd.read_csv('../Data/' + moves_name)
    ratings = pd.read_csv('../Data/' + ratings_name)
    n_movies = len(movies)
    n_ratings = len(ratings)
    last_movies = int(movies.iloc[-1].movieId)
    last_users = int(ratings.iloc[-1].userId)
    dataMat = np.zeros((last_users, last_movies))
    for i in range(len(ratings)):
        rating = ratings.loc[i]
        dataMat[int(rating.userId) - 1, int(rating.movieId) - 1] = rating['rating']
        pass
    return dataMat

要注意的是,這裏電影的ID不是連在一起的,有點坑。

2.梯度上升做optimization

def gradDscent(dataMat, k, alpha, beta, maxIter):
    '''

    :param dataMat:dataSet
    :param k: params of the matrix fatorization
    :param alphs: learning rate
    :param beta: regularization params
    :param maxIter: maxiter
    :return:
    '''
    print('start training......')
    m, n = np.shape(dataMat)
    p = np.mat(np.random.random((m, k)))
    q = np.mat(np.random.random((k, n)))

    for step in range(maxIter):
        for i in range(m):
            for j in range(n):
                if dataMat[i, j] > 0:
                    error = dataMat[i, j]
                    for r in range(k):
                        error = error - p[i, r]*q[r, j]
                    for r in range(k):
                        p[i, r] = p[i, r] + alpha * (2 * error * q[r, j] - beta * p[i, r])
                        q[r, j] = q[r, j] + alpha * (2 * error * p[i, r] - beta * q[r, j])
        loss = 0.0
        for i in range(m):
            for j in range(n):
                if dataMat[i, j] > 0:
                    error = 0.0
                    for r in range(k):
                        error = error + p[i, r] * q[r, j]
                    loss = np.power((dataMat[i, j] - error), 2)
                    for r in range(k):
                        loss = loss + beta * (p[i, r]*p[i, r] + q[r, j]*q[r, j])/2
        if loss < 0.001:
            break
        print('step : ', step, ' loss : ', loss)
    return p, q

並沒有使用SGD,只是完全迭代。k就是分解的因子了,可以5個10個等等。中間都是按部就班的根據公式來即可。但是中間加入的正則化,求導也是很容易得到結果的。

p, q = gradDscent(dataMat, 10, 0.0002, 0.02, 100000)

跑的太慢了,使用直接看loss就好了,畢竟很簡單而已。


10624272-6d2824d4c7274bb1.png

都是不斷減小的。

附上GitHub代碼:https://github.com/GreenArrow2017/MachineLearning/tree/master/MachineLearning/MatrixFactorization

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章