用半監督算法作文本分類

做者:煉己者算法

歡迎你們訪問 個人簡書 以及 個人博客
本博客全部內容以學習、研究和分享爲主,如需轉載,請聯繫本人,標明做者和出處,而且是非商業用途,謝謝!app


摘要:本文主要講述了用半監督算法作文本分類(二分類),主要借鑑了sklearn的一個例子——用半監督算法作數字識別 。先說結論,這是一個失敗的例子,訓練到第15000條就不行了,就報錯了。若是你的數據量不是很大的話,能夠操做一下。這裏面有不少值得學習的地方,尤爲是關於文本的預處理。後續還會更新,把這條路打通。學習


一. 操做流程

  • 一共100萬條數據,標註7000條,剩下的所有標註爲-1
  • 對文本進行預處理(jieba分詞,去掉停用詞等操做)
  • 用gensim庫將處理好的文本轉爲TFIDF向量
  • 利用scipy庫轉換TFIDF向量的格式,使其能夠被sklearn庫的算法包訓練
  • 把預處理的數據輸入到模型中進行訓練
  • 獲得前500個不肯定的數據,對其進行人工標註,而後再添加回訓練集
  • 重複以上過程,直到你的分類器變得好,符合你的要求

二. 正文

前面的jieba分詞,去停用詞的操做我就不說了,比較容易。在這裏就分享一下怎麼把gensim訓練的TFIDF向量轉爲sklearn庫算法包所要求的格式吧。spa

說到這裏,你們確定會問,幹啥這麼麻煩,直接調用sklearn計算TFIDF不就完了。緣由很簡單,一開始我也是這麼幹的,而後報了內存錯誤,當時我猜想是由於向量的維度太大了,由於sklearn計算出來的TFIDF向量每句話的維度達到了30000多維,因此我打算借用gensim來訓練TFIDF向量,它有個好處,就是能夠指定維度的大小。code

那麼如何計算TFIDF向量呢?
你們也能夠看這篇文章——用不一樣的方法計算TFIDF值blog

1. 用gensim訓練TFIDF向量,而且保存起來,省的你下次用的時候還要再跑一遍代碼

from gensim import corpora
dictionary = corpora.Dictionary(word_list)
new_corpus = [dictionary.doc2bow(text) for text in word_list]

from gensim import models
tfidf = models.TfidfModel(new_corpus)
tfidf.save('my_model.tfidf')

2.載入模型,訓練你的數據,獲得TFIDF向量

tfidf = models.TfidfModel.load('my_model.tfidf')
tfidf_vec = []
for i in range(len(words)):
    string = words[i]
    string_bow = dictionary.doc2bow(string.split())
    string_tfidf = tfidf[string_bow]
    tfidf_vec.append(string_tfidf)

此時獲得的TFIDF向量是這樣的

  • 元組的形式——(數據的id,TFIDF向量)
  • 這樣的格式sklearn的算法包是沒法訓練的,怎麼辦?
[[(0, 0.44219328927835233),
  (1, 0.5488488134902755),
  (2, 0.28062764931589196),
  (3, 0.5488488134902755),
  (4, 0.3510600763648036)],
 [(5, 0.2952063480959091),
  (6, 0.3085138762011414),
  (7, 0.269806482343891),
  (8, 0.21686460370108193),
  (9, 0.4621642239026475),
  (10, 0.5515758504022944),
  (11, 0.4242816486479956)],
......]

3.利用lsi模型指定維度

lsi_model = models.LsiModel(corpus = tfidf_vec,id2word = dictionary,num_topics=2)
lsi_vec = []
for i in range(len(words)):
    string = words[i]
    string_bow = dictionary.doc2bow(string.split())
    string_lsi = lsi_model[string_bow]
    lsi_vec.append(string_lsi)

此時的TFIDF向量是這樣的

[[(0, 9.98164139346566e-06), (1, 0.00017488533996265734)],
 [(0, 0.004624808817003378), (1, 0.0052712355563472625)],
 [(0, 0.005992863818284904), (1, 0.0028891269605347066)],
 [(0, 0.008813713819377964), (1, 0.004300294830187425)],
 [(0, 0.0010709978891676652), (1, 0.004264312831567625)],
 [(0, 0.005647948200006063), (1, 0.005816420698368305)],
 [(0, 1.1749284917071102e-05), (1, 0.0003525210498926822)],
 [(0, 0.05046596444596279), (1, 0.03750969796637345)],
 [(0, 0.0007876011346475033), (1, 0.008538972615602887)],
......]

4.經過scipy模塊將數據處理爲sklearn可訓練的格式

from scipy.sparse import csr_matrix
data = []
rows = []
cols = []
line_count = 0
for line in lsi_vec:
    for elem in line:
        rows.append(line_count)
        cols.append(elem[0])
        data.append(elem[1])
    line_count += 1
lsi_sparse_matrix = csr_matrix((data,(rows,cols))) # 稀疏向量
lsi_matrix = lsi_sparse_matrix.toarray() # 密集向量

lsi_matrix以下所示

  • 這即是符合sklearn的格式要求了
Out[53]:
array([[9.98164139e-06, 1.74885340e-04],
       [4.62480882e-03, 5.27123556e-03],
       [5.99286382e-03, 2.88912696e-03],
       ...,
       [1.85861559e-02, 3.24888917e-01],
       [8.07737902e-04, 5.45659458e-03],
       [2.61926460e-03, 2.30210522e-02]])

5.調用sklearn的半監督算法對數據進行訓練

  • 經過如下代碼即可以獲得前2000條標籤最不肯定的數據的索引,而後根據索引找到對應的數據,就能夠對其從新人工標註了
  • 把標好的數據再加回去,循環往復,直到你滿意爲止
  • 固然這裏面你得本身標註1000條數據,而後再把它們賦值爲-1,僞裝不知道,等訓練完,獲得預測的結果,再與你本身標註的真實值進行比對計算,就能夠知道效果的好壞了
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
from sklearn.semi_supervised import label_propagation

y = list(result.label.values)
from scipy.sparse.csgraph import *

n_total_samples = len(y) # 1571794
n_labeled_points = 7804 # 標註好的數據共10條,只訓練10個帶標籤的數據模型
unlabeled_indices = np.arange(n_total_samples)[n_labeled_points:] # 未標註的數據

lp_model = label_propagation.LabelSpreading() # 訓練模型
lp_model.fit(lsi_matrix,y)
    
predicted_labels = lp_model.transduction_[unlabeled_indices] # 預測的標籤
    
    # 計算被轉換的標籤的分佈的熵
    # lp_model.label_distributions_ : array,shape=[n_samples,n_classes]
    # Categorical distribution for each item
    
pred_entropies = stats.distributions.entropy(
lp_model.label_distributions_.T)
    
    # 選擇分類器最不肯定的前2000位數字的索引
uncertainty_index = np.argsort(pred_entropies)[::1]
uncertainty_index = uncertainty_index[
    np.in1d(uncertainty_index,unlabeled_indices)][:2000] 

print(uncertainty_index)

三. 結果與討論

我最後沒有繼續往下作了,由於個人數據量很大,只能訓練這麼點數據沒有意義,以上即是個人思路,但願會對你們有所幫助,後續我會更新新的方法來操做這個事情索引

相關文章
相關標籤/搜索