作者:煉己者
歡迎大家訪問 我的簡書 以及 我的博客
本博客所有內容以學習、研究和分享為主,如需轉載,請聯系本人,標明作者和出處,並且是非商業用途,謝謝!
摘要:本文主要講述了用半監督算法做文本分類(二分類),主要借鑒了sklearn的一個例子——用半監督算法做數字識別 。先說結論,這是一個失敗的例子,訓練到第15000條就不行了,就報錯了。如果你的數據量不是很大的話,可以操作一下。這里面有很多值得學習的地方,尤其是關於文本的預處理。后續還會更新,把這條路打通。
一. 操作流程
- 一共100萬條數據,標注7000條,剩下的全部標注為-1
- 對文本進行預處理(jieba分詞,去掉停用詞等操作)
- 用gensim庫將處理好的文本轉為TFIDF向量
- 利用scipy庫轉換TFIDF向量的格式,使其可以被sklearn庫的算法包訓練
- 把預處理的數據輸入到模型中進行訓練
- 得到前500個不確定的數據,對其進行人工標注,然后再添加回訓練集
- 重復以上過程,直到你的分類器變得好,符合你的要求
二. 正文
前面的jieba分詞,去停用詞的操作我就不說了,比較容易。在這里就分享一下怎么把gensim訓練的TFIDF向量轉為sklearn庫算法包所要求的格式吧。
說到這里,大家肯定會問,干啥這么麻煩,直接調用sklearn計算TFIDF不就完了。原因很簡單,一開始我也是這么干的,然后報了內存錯誤,當時我猜測是因為向量的維度太大了,因為sklearn計算出來的TFIDF向量每句話的維度達到了30000多維,所以我打算借用gensim來訓練TFIDF向量,它有個好處,就是可以指定維度的大小。
那么如何計算TFIDF向量呢?
大家也可以看這篇文章——用不同的方法計算TFIDF值
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)
三. 結果與討論
我最后沒有繼續往下做了,因為我的數據量很大,只能訓練這么點數據沒有意義,以上便是我的思路,希望會對大家有所幫助,后續我會更新新的方法來操作這個事情
