自然語言處理真實項目實戰(zhàn)(20170830)

前言

本文根據(jù)實際項目撰寫争舞,由于項目保密要求士修,源代碼將進(jìn)行一定程度的刪減枷遂。
本文撰寫的目的是進(jìn)行公司培訓(xùn),請勿以任何形式進(jìn)行轉(zhuǎn)載棋嘲。
由于是日語項目酒唉,用到的分詞軟件等,在中文任務(wù)中需要替換為相應(yīng)的中文分詞軟件沸移。例如結(jié)巴分詞 : https://github.com/fxsjy/jieba

前提知識和術(shù)語解釋

如果需要獲得更多知識痪伦,請自行百度,谷歌雹锣。中文資料不是很多网沾,有能力請閱讀相關(guān)論文資料。

術(shù)語縮寫

PV-DM: Distributed Memory Model of Paragraph Vectors 句向量的分布記憶模型
PV-DBOW: Distributed Bag of Words version of Paragraph Vector 句向量的分布詞袋
注意:distributed這里是區(qū)別于one-hot

distrubted 表示一個個體用幾個編碼單元而不是一個編碼單元表示蕊爵,即一個個體分布在幾個編碼單元上辉哥,主要是相對one-hot編碼中一個編碼單元表示一個個體。

one-hot representation與distributed representation學(xué)習(xí)筆記

余弦相似度

余弦相似度攒射,又稱為余弦相似性醋旦,是通過計算兩個向量的夾角余弦值來評估他們的相似度。余弦相似度將向量根據(jù)坐標(biāo)值会放,繪制到向量空間中饲齐,如最常見的二維空間。

余弦相似度

將向量根據(jù)坐標(biāo)值咧最,繪制到向量空間中捂人。如最常見的二維空間〈笆校 
求得他們的夾角先慷,并得出夾角對應(yīng)的余弦值,此余弦值就可以用來表征咨察,這兩個向量的相似性论熙。夾角越小,余弦值越接近于1摄狱,它們的方向更加吻合脓诡,則越相似无午。

單位矢量

單位向量是指模等于1的向量。由于是非零向量祝谚,單位向量具有確定的方向宪迟。一個非零向量除以它的模,可得所需單位向量交惯。單位向量有無數(shù)個次泽。
(矢量和向量是同一個意思,Vector席爽,這里習(xí)慣用矢量這個詞語)

gensim.matutils.unitvec(vec, norm='l2')
Scale a vector to unit length. The only exception is the zero vector, which is returned back unchanged.
Output will be in the same format as input (i.e., gensim vector=>gensim vector, or np array=>np array, scipy.sparse=>scipy.sparse).

向量加減

平行四邊形定則解決向量加法的方法:將兩個向量平移至公共起點意荤,以向量的兩條邊作平行四邊形,結(jié)果為公共起點的對角線只锻。
平行四邊形定則解決向量減法的方法:將兩個向量平移至公共起點玖像,以向量的兩條邊作平行四邊形,結(jié)果由減向量的終點指向被減向量的終點齐饮。

image.png

向量點積

代數(shù)定義

設(shè)二維空間內(nèi)有兩個向量


捐寥,定義它們的數(shù)量積(又叫內(nèi)積、點積)為以下實數(shù):



更一般地祖驱,n維向量的內(nèi)積定義如下:

點乘的結(jié)果就是兩個向量的模相乘握恳,然后再與這兩個向量的夾角的余弦值相乘「牛或者說是兩個向量的各個分量分別相乘的結(jié)果的和睡互。很明顯,點乘的結(jié)果就是一個數(shù)陵像,這個數(shù)對我們分析這兩個向量的特點很有幫助就珠。如果點乘的結(jié)果為0,那么這兩個向量互相垂直;如果結(jié)果大于0醒颖,那么這兩個向量的夾角小于90度;如果結(jié)果小于0妻怎,那么這兩個向量的夾角大于90度。

TF-IDF

TF-IDF是一種統(tǒng)計方法泞歉,用以評估一字詞對于一個文件集或一個語料庫中的其中一份文件的重要程度逼侦。字詞的重要性隨著它在文件中出現(xiàn)的次數(shù)成正比增加,但同時會隨著它在語料庫中出現(xiàn)的頻率成反比下降腰耙。TF-IDF加權(quán)的各種形式常被搜索引擎應(yīng)用榛丢,作為文件與用戶查詢之間相關(guān)程度的度量或評級。除了TF-IDF以外挺庞,因特網(wǎng)上的搜索引擎還會使用基于鏈接分析的評級方法晰赞,以確定文件在搜尋結(jié)果中出現(xiàn)的順序。
在gensim里面有多個主題模型,TfidfModel可以直接用庫來計算.

from gensim.models import TfidfModel, LsiModel, LdaModel
corpus = [dic.doc2bow(text) for text in words]
tfidf = models.TfidfModel(corpus)

LSI(latent semantic index)

潛在語義索引

我們希望找到一種模型掖鱼,能夠捕獲到單詞之間的相關(guān)性然走。如果兩個單詞之間有很強(qiáng)的相關(guān)性,那么當(dāng)一個單詞出現(xiàn)時戏挡,往往意味著另一個單詞也應(yīng)該出現(xiàn)(同義詞)芍瑞;反之,如果查詢語句或者文檔中的某個單詞和其他單詞的相關(guān)性都不大褐墅,那么這個詞很可能表示的是另外一個意思(比如在討論互聯(lián)網(wǎng)的文章中拆檬,Apple更可能指的是Apple公司,而不是水果) 掌栅。

LSA(LSI)使用SVD來對單詞-文檔矩陣進(jìn)行分解秩仆。SVD可以看作是從單詞-文檔矩陣中發(fā)現(xiàn)不相關(guān)的索引變量(因子),將原來的數(shù)據(jù)映射到語義空間內(nèi)猾封。在單詞-文檔矩陣中不相似的兩個文檔,可能在語義空間內(nèi)比較相似噪珊。

SVD晌缘,亦即奇異值分解,是對矩陣進(jìn)行分解的一種方法痢站,一個td維的矩陣(單詞-文檔矩陣)X磷箕,可以分解為TSDT,其中T為tm維矩陣阵难,T中的每一列稱為左奇異向量(left singular bector)岳枷,S為mm維對角矩陣,每個值稱為奇異值(singular value)呜叫,D為dm維矩陣,D中的每一列稱為右奇異向量空繁。在對單詞文檔矩陣X做SVD分解之后,我們只保存S中最大的K個奇異值朱庆,以及T和D中對應(yīng)的K個奇異向量盛泡,K個奇異值構(gòu)成新的對角矩陣S’,K個左奇異向量和右奇異向量構(gòu)成新的矩陣T’和D’:X’=T’S’D’T形成了一個新的t*d矩陣娱颊。

LDA

LDA(Latent Dirichlet Allocation)是一種文檔主題生成模型傲诵,也稱為一個三層貝葉斯概率模型,包含詞箱硕、主題和文檔三層結(jié)構(gòu)拴竹。所謂生成模型,就是說剧罩,我們認(rèn)為一篇文章的每個詞都是通過“以一定概率選擇了某個主題栓拜,并從這個主題中以一定概率選擇某個詞語”這樣一個過程得到。文檔到主題服從多項式分布,主題到詞服從多項式分布菱属。
LDA是一種非監(jiān)督機(jī)器學(xué)習(xí)技術(shù)钳榨,可以用來識別大規(guī)模文檔集(document collection)或語料庫(corpus)中潛藏的主題信息。它采用了詞袋(bag of words)的方法纽门,這種方法將每一篇文檔視為一個詞頻向量薛耻,從而將文本信息轉(zhuǎn)化為了易于建模的數(shù)字信息。但是詞袋方法沒有考慮詞與詞之間的順序赏陵,這簡化了問題的復(fù)雜性饼齿,同時也為模型的改進(jìn)提供了契機(jī)。每一篇文檔代表了一些主題所構(gòu)成的一個概率分布蝙搔,而每一個主題又代表了很多單詞所構(gòu)成的一個概率分布缕溉。

請不要將自然語言處理的LDA和機(jī)器學(xué)習(xí)的LDA混淆(Linear Discriminant Analysis, 以下簡稱LDA)

Doc2Vec

在自然語言處理中,一個很重要的技術(shù)手段就是將文檔轉(zhuǎn)換為一個矢量吃型,這個過程一般是使用gensim這個庫進(jìn)行處理的证鸥。
gensim官網(wǎng)地址
如果你需要Java版本的Doc2Vec:
https://github.com/NLPchina/Word2VEC_java

作為一個處理可變長度文本的總結(jié)性方法,Quoc Le 和 Tomas Mikolov 提出了 Doc2Vec方法勤晚。除了增加一個段落向量以外枉层,這個方法幾乎等同于 Word2Vec。和 Word2Vec 一樣赐写,該模型也存在兩種方法:Distributed Memory(DM) 和 Distributed Bag of Words(DBOW)鸟蜡。DM 試圖在給定上下文和段落向量的情況下預(yù)測單詞的概率。在一個句子或者文檔的訓(xùn)練過程中挺邀,段落 ID 保持不變揉忘,共享著同一個段落向量。DBOW 則在僅給定段落向量的情況下預(yù)測段落中一組隨機(jī)單詞的概率端铛。

Token

Token在詞法分析中是標(biāo)記的意思泣矛。自然語言處理中,一般來說沦补,Token代表“詞”乳蓄。自然語言預(yù)處理中,一個很重要的步驟就是將你收集的句子進(jìn)行分詞夕膀,將一個句子分解成“詞”的列表虚倒。

交叉驗證

交叉驗證(Cross validation),有時亦稱循環(huán)估計产舞, 是一種統(tǒng)計學(xué)上將數(shù)據(jù)樣本切割成較小子集的實用方法魂奥。于是可以先在一個子集上做分析, 而其它子集則用來做后續(xù)對此分析的確認(rèn)及驗證易猫。 一開始的子集被稱為訓(xùn)練集耻煤。而其它的子集則被稱為驗證集或測試集。交叉驗證是一種評估統(tǒng)計分析、機(jī)器學(xué)習(xí)算法對獨(dú)立于訓(xùn)練數(shù)據(jù)的數(shù)據(jù)集的泛化能力(generalize)
least-one-out cross-validation(loocv)
假設(shè)dataset中有n個樣本哈蝇,那LOOCV也就是n-CV棺妓,意思是每個樣本單獨(dú)作為一次測試集,剩余n-1個樣本則做為訓(xùn)練集炮赦。
優(yōu)點:
1)每一回合中幾乎所有的樣本皆用于訓(xùn)練model怜跑,因此最接近母體樣本的分布,估測所得的generalization error比較可靠吠勘。
2)實驗過程中沒有隨機(jī)因素會影響實驗數(shù)據(jù)性芬,確保實驗過程是可以被復(fù)制的。
但LOOCV的缺點則是計算成本高剧防,為需要建立的models數(shù)量與總樣本數(shù)量相同植锉,當(dāng)總樣本數(shù)量相當(dāng)多時,LOOCV在實作上便有困難峭拘,除非每次訓(xùn)練model的速度很快俊庇,或是可以用平行化計算減少計算所需的時間。

LinearRegression

[sklearn學(xué)習(xí)]linear_model.LinearRegression

from sklearn.linear_model import LinearRegression

fit(X, y[, n_jobs]) 對訓(xùn)練集X, y進(jìn)行訓(xùn)練棚唆。是對scipy.linalg.lstsq的封裝
score(X, y[,]sample_weight) 定義為(1-u/v)暇赤,其中u = ((y_true - y_pred)**2).sum(),而v=((y_true-y_true.mean())**2).mean()
最好的得分為1.0宵凌,一般的得分都比1.0低,得分越低代表結(jié)果越差止后。
其中sample_weight為(samples_n,)形狀的向量瞎惫,可以指定對于某些sample的權(quán)值,如果覺得某些數(shù)據(jù)比較重要译株,可以將其的權(quán)值設(shè)置的大一些瓜喇。

代碼和處理流程

語料庫的準(zhǔn)備

語料庫的準(zhǔn)備,就是將你準(zhǔn)備好的文章庫歉糜,轉(zhuǎn)換為一個語料庫乘寒。
你的文章一般會被保存為TaggedDocument,也就是帶有標(biāo)簽的文檔匪补。
一篇文章對應(yīng)著一個TaggedDocument對象伞辛。
TaggedDocument里面存放的是Token列表和Tag:
其中Token列表就是將文章通過分詞軟件分成的詞語的列表,Tag這里保存著原來文章的編號夯缺。
這里的Tag也可以是文檔的標(biāo)題蚤氏,或者任何可以代表文檔的Paragraph Id。
下面這個代碼中 tdocs變量表示一個TaggedDocument數(shù)組踊兜。
注意:在gensim以前版本中TaggedDocument是LabeledSentence

corpus = Doc2Vec(tdocs, dm=1, dm_mean=1,
                 size=300, window=8, min_count=2, workers=4, iter=20)
corpus.save(os.path.join(WORK_DIR, 'base-pv_dm.mdl'))

關(guān)于這個函數(shù)的參數(shù)介紹竿滨,可以參考這里,全英文非常晦澀難懂的介紹:
https://radimrehurek.com/gensim/models/doc2vec.html

dm defines the training algorithm. By default (dm=1), ‘distributed memory’ (PV-DM) is used. Otherwise, distributed bag of words (PV-DBOW) is employed.
dm:定義了訓(xùn)練的算法于游,默認(rèn)值為1毁葱,使用 ‘distributed memory’方法,不然則使用分布式的“bag of words” 方法贰剥。(dm倾剿,就是distributed memory的意思)
size is the dimensionality of the feature vectors.
size:是向量的維度,本項目維度設(shè)定是300鸠澈。維度這個參數(shù)也是需要通過大量實驗獲得最佳的值柱告。
dm_mean = if 0 (default), use the sum of the context word vectors. If 1, use the mean. Only applies when dm is used in non-concatenative mode.
dm_mean:如果是默認(rèn)值0,則使用上下文向量的和(SUM)笑陈,如果是1的話际度,則使用上下文向量的平均值。這個僅僅在dm使用non-concatenative的模式才發(fā)生效果涵妥。
workers = use this many worker threads to train the model (=faster training with multicore machines).
如果是多核處理器乖菱,這里可以指定并行數(shù)
iter = number of iterations (epochs) over the corpus. The default inherited from Word2Vec is 5, but values of 10 or 20 are common in published ‘Paragraph Vector’ experiments.
迭代次數(shù):默認(rèn)的迭代次數(shù)是5,但是最佳實踐應(yīng)該是10或者20.
min_count = ignore all words with total frequency lower than this.
如果出現(xiàn)頻率少于min_count蓬网,則忽略
window is the maximum distance between the predicted word and context words used for prediction within a document.
window是被預(yù)測詞語和上下文詞語在同一個文檔中的最大的距離窒所。

語料庫也是支持序列化操作的,語料庫可以保存為磁盤上的文件:

Save the object to file (also see load).
fname_or_handle is either a string specifying the file name to save to, or an open file-like object which can be written to. If the object is a file handle, no special array handling will be performed; all attributes will be saved to the same file.

語料庫建成之后帆锋,就可以進(jìn)行一些有趣的檢索了吵取。
例如參考文檔 [Algorithm & NLP] 文本深度表示模型——word2vec&doc2vec詞向量模型 中的句子相似度實驗:

下面是sentence2vec的結(jié)果示例。先利用中文sentence語料訓(xùn)練句向量锯厢,然后通過計算句向量之間的cosine值皮官,得到最相似的句子∈导可以看到句向量在對句子的語義表征上還是相當(dāng)驚嘆的捺氢。


句子相似度結(jié)果

相似檢索

這里的相似度檢索是指,給定一個正面的句子剪撬,然后檢索和其相似度最大的句子摄乒。
當(dāng)然,這里也可以指定一個負(fù)面的句子残黑,也就是和這個句子越不相似越好馍佑。
這里有一個限制,如果正面的句子和負(fù)面的句子萍摊,進(jìn)行分詞之后挤茄,沒有一個詞語是被訓(xùn)練過的(被訓(xùn)練過的詞語,是指語料庫里面存在的詞語)冰木,則無法進(jìn)行操作穷劈。

具體在求相似度的操作之前笼恰,檢索用向量需要進(jìn)行一下處理。
假設(shè)positive變量是一個數(shù)組歇终,數(shù)組里面存放著正面的Token社证。
corpus[token]表示token的矢量,這里對矢量進(jìn)行按列求和评凝,結(jié)果是一個和token維度一樣的矢量追葡。換句話說,就是將多個矢量合并為單個矢量奕短。(Token矢量的求和矢量)
然后將上面那個“Token矢量的求和矢量”宜肉,和新的positive的推測矢量進(jìn)行相加,獲得一個新的"求相似度用矢量"翎碑。

(Negative和Positive類似)

    p = np.array([ corpus[token] for token in positive ]).sum(axis=0)
    p = p + corpus.infer_vector(positive, steps=20)
    n = np.array([ corpus[token] for token in negative ]).sum(axis=0)
    n = n + corpus.infer_vector(negative, steps=20)

在語料庫對象(Document Model)中有一個很有用的方法infer_vector谬返,這個方法可以基于當(dāng)前的文檔模型快速,將一個文檔轉(zhuǎn)換(按照模型推測)成一個矢量日杈。

infer_vector
(doc_words, alpha=0.1, min_alpha=0.0001, steps=5)
Infer a vector for given post-bulk training document.
Document should be a list of (word) tokens.

在機(jī)器學(xué)習(xí)界遣铝,有兩種機(jī)器學(xué)習(xí)方式,一種是Online的莉擒,一種是Offline的酿炸。Online的方式,模型可以實時更新涨冀,新的樣本會被實時進(jìn)行訓(xùn)練填硕,訓(xùn)練結(jié)果也實時反映到模型中去。Offline的方式鹿鳖,如果有新的樣本廷支,則需要將新老樣本放在一起,重新進(jìn)行訓(xùn)練栓辜。這里的話,模型無法進(jìn)行Online的訓(xùn)練垛孔,所以新的樣本藕甩,只是基于訓(xùn)練好的模型,被轉(zhuǎn)換(推測 Infer周荐,有些類似于預(yù)測Predict)為一個矢量狭莱。

相似度計算的核心方法是most_similar

most_similar
(positive=[], negative=[], topn=10, clip_start=0, clip_end=None, indexer=None)
Find the top-N most similar docvecs known from training. Positive docs contribute positively towards the similarity, negative docs negatively.
This method computes cosine similarity between a simple mean of the projection weight vectors of the given docs. Docs may be specified as vectors, integer indexes of trained docvecs, or if the documents were originally presented with string tags, by the corresponding tags.
The ‘clip_start’ and ‘clip_end’ allow limiting results to a particular contiguous range of the underlying doctag_syn0norm vectors. (This may be useful if the ordering there was chosen to be significant, such as more popular tag IDs in lower indexes.)
尋找最相似的N個文檔。正面(Positive)文檔向相似度貢獻(xiàn)正面的值概作,負(fù)面(Negative)文檔貢獻(xiàn)負(fù)面的值腋妙。這個方法通過計算給定文章的矢量的加權(quán)平均值的余弦相似度來給出結(jié)果⊙堕牛可以通過矢量骤素,被訓(xùn)練過的文檔矢量的下標(biāo)匙睹,或者原始的字符串標(biāo)簽來指定文檔(正面或者負(fù)面文檔)。
‘clip_start’ 和 ‘clip_end’則是指定了相似度檢索的范圍济竹。

官方文檔其實說明的不是很清楚痕檬,很多地方還是不容易理解。
topn這個參數(shù)應(yīng)該沒有問題送浊,你想返回最相似的多少個梦谜,這里就指定多少即可。
對于positive和nagative的指定袭景,首先明確一下唁桩,這里必須是一個數(shù)組,即使只有一個值耸棒,也必須是數(shù)組荒澡。
positive和nagative數(shù)組里面的值,可以是:
1.具體的文檔的矢量
2.被訓(xùn)練過的文檔的下標(biāo)
3.文檔的Tag字符榆纽。(本項目里面的Tag就是文檔的編號)
具體到這個項目中仰猖,Positive則是上文提到的"求相似度用矢量"
‘clip_start’ 和 ‘clip_end’則是指定了相似度檢索的范圍奈籽,這個一般是用來限定檢索范圍饥侵,例如只想在1年或者3年的資料中進(jìn)行檢索。

情感模型建立

MiniBatchKMeans

情感分析是建立在文檔的聚類基礎(chǔ)上的衣屏。由于計算量比較巨大躏升,項目使用的是MiniBatchKMeans。
在有效減少計算時間的同時狼忱,也能保證計算誤差在可接受范圍中膨疏。
項目中使用的是Leave-One-Out Cross Validation,每次將一個樣本作為測試集钻弄,一共進(jìn)行n次交叉驗證佃却。

K-Means算法是常用的聚類算法,但其算法本身存在一定的問題窘俺,例如在大數(shù)據(jù)量下的計算時間過長就是一個重要問題饲帅。為此,Mini Batch K-Means瘤泪,這個基于K-Means的變種聚類算法應(yīng)運(yùn)而生灶泵。


Mini Batch K-Means
from sklearn.cluster import MiniBatchKMeans
X_km_norm = [ unitvec(x) for x in X_km ]    #轉(zhuǎn)為單位向量
km_all = MiniBatchKMeans(n_clusters=8)      #分為8個簇
km_all.fit(X_km_norm)                       #計算8個簇的質(zhì)心

fit(X, y=None)
Compute the centroids on X by chunking it into mini-batches.
fit擬合操作,實際上就是計算每個簇的質(zhì)心对途。
所以說赦邻,如果簇只有一個的話,擬合的意義是求出整個數(shù)據(jù)的質(zhì)心实檀。
predict(X)
Predict the closest cluster each sample in X belongs to.
predict預(yù)測操作惶洲,是給出每個樣本屬于哪個簇的結(jié)果

訓(xùn)練樣本的分類和整理

項目中將所有樣本按照時間分為:1年期樣本和3年期樣本按声。同時根據(jù)其他業(yè)務(wù)規(guī)則進(jìn)行了分類(分類規(guī)則需要保密)。由于收集樣本的渠道不同(不同的公司湃鹊,組織提供的樣本數(shù)據(jù)),所有的樣本還需要進(jìn)行Remove Common Factor的操作:
1.樣本的分類并沒有按照渠道進(jìn)行分類儒喊,所以這里同一收集渠道的樣本,也會被分在不同的類中
2.所有分類樣本币呵,例如1年期怀愧,3年期的樣本,都必須進(jìn)行Remove Common Factor
3.同一樣本可能同時存在于不同分類組里面余赢,因為有些是按照時間分類的芯义,有些是按照業(yè)務(wù)分類的,分類的維度不同妻柒。

代碼的邏輯如下:
fit:XX[tag]里面的tag表示收集渠道扛拨,XX[tag]表示某個渠道的樣本矢量數(shù)組:
_cf[tag]:表示某個渠道的Common Factor矢量,這里使用np.array(XX[tag]).mean(axis=0)按列求均值獲得的举塔。每一個渠道有一個Common Factor矢量绑警。
remove代碼則是將某個渠道里面所有的矢量,都剪去Common Factor矢量
(代碼有刪減央渣,原來代碼里面有對于未知渠道的防御代碼计盒,這里已經(jīng)簡化)

class CommonFactor(object):
    def __init__(self):
        self._cf = {} 

    def fit(self, XX):
        for tag in XX:
            if XX[tag]:
                shape = XX[tag][0].shape
                self._cf[tag] = np.array(XX[tag]).mean(axis=0)
        return self

    def remove(self, XX):
        return dict([ (tag, [ x - self._cf[tag] for x in XX[tag] ])
                      for tag in XX ])

在我們的訓(xùn)練樣本(document)中,有一個權(quán)重(weight)屬性芽丹,這個屬性是和業(yè)務(wù)有關(guān)的北启,也是這個項目需要進(jìn)行預(yù)測的。例如拔第,有一些用戶對于商品的評論咕村,可以看作一個訓(xùn)練樣本(document),這個商品的銷量可以看作權(quán)重屬性(weight)蚊俺。我們需要訓(xùn)練的模型就是獲得一個商品評論和銷量的關(guān)系模型懈涛,利用這個模型能夠通過商品評論去預(yù)測一個商品的銷量。
這里假設(shè)我們的權(quán)重分為5個級別泳猬,-2(無人問津)肩钠,-1(滯銷),0(正常)暂殖,1(暢銷),2(爆款) 分別對應(yīng)5種銷量級別当纱。
然后我們將所有樣本通過MiniBatchKMeans分到8個不同簇里面呛每,注意,這里的簇數(shù)和級別數(shù)是不一樣的坡氯。
另外請注意晨横,訓(xùn)練后的簇洋腮,其簇的編號和權(quán)重也是沒有任何關(guān)系的,簇號0-7和權(quán)重-2到2手形,完全是兩個獨(dú)立的體系酪穿。

    def fit(self, X, y):
        X = np.array(X)
        y = np.array(y).reshape(-1, 1)
        size_x = X[0].shape[0]
        self.km.fit([ unitvec(x) for x in X ])
        #為評分Score進(jìn)行數(shù)據(jù)整理
        n_clusters = self.km.get_params()['n_clusters']
        self.p_vec = np.zeros((n_clusters, size_x), np.float32)
        self.n_vec = np.zeros((n_clusters, size_x), np.float32)
        self.pn_vec = np.zeros((n_clusters, size_x), np.float32)
        #簇的編號和權(quán)重沒有任何關(guān)系
        p_vecs = [ [] for i in range(n_clusters)]                   #存放每個簇的Positive矢量
        n_vecs = [ [] for i in range(n_clusters)]                   #存放每個簇的Nagative矢量
        for c, x, (w,) in zip(self.km.predict(X), X, y):
            ext = [x] * abs(w)                                      #矢量 * 權(quán)重的絕對值
            if w > 0:
                p_vecs[c].extend(ext)                               #weight[0]是正數(shù)
            else:
                n_vecs[c].extend(ext)                               #weight[0]是負(fù)數(shù)和零
        for c in range(n_clusters):
            if len(p_vecs[c]) == 0:
                p_vecs[c] = [ np.zeros(size_x, np.float32) ]
            if len(n_vecs[c]) == 0:
                n_vecs[c] = [ np.zeros(size_x, np.float32) ]
            self.p_vec[c] = np.array(p_vecs[c]).mean(axis=0)        #簇的權(quán)重為正數(shù)的矢量均值
            self.n_vec[c] = np.array(n_vecs[c]).mean(axis=0)        #簇的權(quán)重為負(fù)數(shù)的矢量均值
            self.pn_vec[c] = unitvec(self.p_vec[c] - self.n_vec[c]) #簇的正負(fù)差嗜浮,單位矢量化
        return self

同時,我們還需要獲得一個訓(xùn)練的得分(score)

    def predict(self, X):
        if not X: return (([], []), [])
        X = np.array(X)
        res = np.zeros((X.shape[0], 2))
        labels = self.km.predict(X)
        for i, (x, c) in enumerate(zip(X, labels)):
            #pn_vec[c]已經(jīng)單位矢量化了,注意:p_score計算式中的是n_vec预厌,反之亦然
            #負(fù)分?jǐn)?shù) =(待預(yù)測矢量 - 簇的權(quán)重為正數(shù)的矢量均值)點積 單位矢量化簇的正負(fù)差
            p_score = np.dot(unitvec(x - self.n_vec[c]), self.pn_vec[c])    
            #正分?jǐn)?shù) =(待預(yù)測矢量 - 簇的權(quán)重為負(fù)數(shù)的矢量均值)點積 單位矢量化簇的正負(fù)差
            n_score = np.dot(unitvec(x - self.p_vec[c]), -self.pn_vec[c])   
            res[i] = (p_score, n_score)
        return (res, labels)

評價訓(xùn)練得分的部分使用了余弦原理,通過單位向量的點乘(點積)結(jié)果來獲得相似度鼎天。注意晦鞋,一定要將兩個矢量都單位化,轉(zhuǎn)換為模為1的矢量艘虎,這樣點乘出來的結(jié)果才是余弦值唉侄。
負(fù)分?jǐn)?shù) = 矢量單位化(待預(yù)測矢量 - 簇的權(quán)重為正數(shù)的矢量均值)點積 單位矢量化簇的正負(fù)差

負(fù)分?jǐn)?shù)示意圖

Python語言

推薦通過網(wǎng)絡(luò)上的 廖雪鋒的Python教程 學(xué)習(xí)python語法

numpy sum

axis:求和的維。

>>> np.sum([0.5, 1.5])
2.0
>>> np.sum([0.5, 0.7, 0.2, 1.5], dtype=np.int32)
1
>>> np.sum([[0, 1], [0, 5]])
6
>>> np.sum([[0, 1], [0, 5]], axis=0)
array([0, 6])
>>> np.sum([[0, 1], [0, 5]], axis=1)
array([1, 5])

numpy mean

axis:求平均的維野建。

>>> a = np.array([[1, 2], [3, 4]])
>>> np.mean(a)
2.5
>>> np.mean(a, axis=0)
array([ 2.,  3.])
>>> np.mean(a, axis=1)
array([ 1.5,  3.5])

lambda 和浮點數(shù)

python中使用 .1 代表浮點數(shù) 0.1 或者 1. 代表浮點數(shù) 1.0属划。
原因是要保證結(jié)果的精度,防止程序自動強(qiáng)制轉(zhuǎn)換候生。

score = lambda X, y: 1.-((y-X)**2).sum()/((y-y.mean())**2).sum()

percentile

幾つかの數(shù)値データを小さい順に並べたとき同眯、小さい方から數(shù)えて全體のX%に位置する値をXパーセンタイルと言います。
(數(shù)值按照從小到大進(jìn)行排列陶舞,從小的數(shù)字開始計算嗽测,全體數(shù)字的X%的位置,數(shù)值是多少)

例えば10人のクラスがあるとして肿孵、各生徒のテストの點數(shù)が[40, 50, 60, 70, 75, 80, 83, 86, 89, 95]だったとします唠粥。
その時、下から95%に位置する點數(shù)(逆に言うと上位5%に位置する點數(shù))が何點なのか示すものが95パーセンタイルになります停做。

以下晤愧、numpyを使ったサンプルです。

>>> import numpy as np
>>> a = np.array([40, 50, 60, 70, 75, 80, 83, 86, 89, 95])

>>> np.percentile(a, 95) # 95パーセンタイルを求めます(逆に言うと上位5%に位置する點數(shù))
92.299999999999997
# 95パーセンタイルは約92.3點であることがわかります

>>> np.percentile(a, 30) # 30パーセンタイルを求めます(逆に言うと上位70%に位置する點數(shù))
67.0
# 30パーセンタイルは67.0點であることがわかります

numpy的mask

numpy的高級特性蛉腌,可以進(jìn)行數(shù)據(jù)的篩選官份。下面的代碼沒有完全看懂,猜測了一下意思:
n_clusters 是一個數(shù)字烙丛,則c也是一個數(shù)字舅巷。P[下標(biāo)]也應(yīng)該是一個數(shù)字,這個下標(biāo)數(shù)字是由C==c這個條件來獲得的河咽。在C這個數(shù)組里面存放的就是數(shù)字钠右,C[x] == c的時候,x則是需要求出的下標(biāo)忘蟹,P[C==c],則實際上就是P[x].當(dāng)然飒房,這里x應(yīng)該是多個值搁凸,則P[x]的結(jié)果也是一個數(shù)組。

    def score(self, X, y):
        score = lambda X, y: 1.-((y-X)**2).sum()/((y-y.mean())**2).sum()
        #predict的結(jié)果為 (res, labels) 
        #res[i] = (p_score, n_score)
        #則最后的形式是[[p_score, n_score],[labels]]形式
        # P 為 p_score狠毯,N 為 n_score护糖,C為label
        P, N, C = zip(*self.predict(X))
        P = np.array(P)
        N = np.array(N)
        C = np.array(C)
        y = np.array(y).reshape(-1, 1)
        n_clusters = self.km.get_params()['n_clusters']
        res = np.zeros((n_clusters+1, 1))
        #==使用了numpy的mask特性
        for c in range(n_clusters):
            res[c] = score(P[C==c]-N[C==c], y[C==c])
        res[-1] = score(P-N, y)
        return res

參考文檔

數(shù)學(xué)之美:14章 余弦定理和新聞的分類 (吳軍,第二版)
現(xiàn)代情感分析方法
TF-IDF與余弦相似性的應(yīng)用(一):自動提取關(guān)鍵詞
TF-IDF與余弦相似性的應(yīng)用(二):找出相似文章
Sentiment Analysis Using Doc2Vec
[Algorithm & NLP] 文本深度表示模型——word2vec&doc2vec詞向量模型
【転職會議】クチコミをword2vecで自然言語処理して會社を分類してみる
適合大數(shù)據(jù)的聚類算法Mini Batch K-Means
K-means算法及文本聚類實踐
パーセンタイルについて
numpy使い方2スライスとブールインデックス
Word2vec 句向量模型PV-DM與PV-DBOW原論文翻譯
Word2vec 中的數(shù)學(xué)原理詳解
one-hot representation與distributed representation學(xué)習(xí)筆記
詞向量( Distributed Representation)工作原理是什么嚼松?
用 Doc2Vec 得到文檔/段落/句子的向量表達(dá)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嫡良,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子惜颇,更是在濱河造成了極大的恐慌皆刺,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凌摄,死亡現(xiàn)場離奇詭異羡蛾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)锨亏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門痴怨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人器予,你說我怎么就攤上這事浪藻。” “怎么了乾翔?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵爱葵,是天一觀的道長。 經(jīng)常有香客問我反浓,道長萌丈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任雷则,我火速辦了婚禮辆雾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘月劈。我一直安慰自己度迂,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布猜揪。 她就那樣靜靜地躺著惭墓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪而姐。 梳的紋絲不亂的頭發(fā)上诅妹,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼吭狡。 笑死,一個胖子當(dāng)著我的面吹牛丈莺,可吹牛的內(nèi)容都是我干的划煮。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼缔俄,長吁一口氣:“原來是場噩夢啊……” “哼弛秋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起俐载,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤蟹略,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后遏佣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挖炬,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年状婶,在試婚紗的時候發(fā)現(xiàn)自己被綠了意敛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡膛虫,死狀恐怖草姻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情稍刀,我是刑警寧澤撩独,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站账月,受9級特大地震影響综膀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜捶障,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一僧须、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧项炼,春花似錦担平、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拌禾,卻和暖如春取胎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工闻蛀, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留匪傍,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓觉痛,卻偏偏與公主長得像役衡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子薪棒,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容