sklearn半監督學習


標簽: 半監督學習


作者:煉己者
歡迎大家訪問 我的簡書 以及 我的博客
本博客所有內容以學習、研究和分享為主,如需轉載,請聯系本人,標明作者和出處,並且是非商業用途,謝謝!

摘要:半監督學習很重要,為什么呢?因為人工標注數據成本太高,現在大家參加比賽的數據都是標注好的了,那么如果老板給你一份沒有標注的數據,而且有幾百萬條,讓你做個分類什么的,你怎么辦?不可能等標注好數據再去訓練模型吧,所以你得會半監督學習算法。

不過我在這里先打擊大家一下,用sklearn的包做不了大數據量的半監督學習,我用的數據量大概在15000條以上就要報MemoryError錯誤了,這個是我最討厭的錯誤。暫時我還沒有解決的辦法,如果同志們是小數據量,那就用這個做着玩玩吧。大家如果有興趣也可以看一下這篇文章——用半監督算法做文本分類

報MemoryError錯誤怎么辦?sklearn提供這么全的文檔當然會有這部分的考慮啦。看這里——sklearn 中的模型對於大數據集的處理。可以用partial_fit增量式計算,可惜只針對部分算法,對於半監督學習沒有辦法。

好了,該說正題了,最近看了sklearn關於半監督學習的例子,它里面有三個例子,在這里我主要想分享一下第三個例子——用半監督學習算法做數字識別


一. 數據集的解讀

首先我們來看一下這份數據集的特點
煉己者

煉己者

二. 代碼的解讀

sklearn官方例子——用半監督學習做數字識別

我們來看一下操作流程

  • 一共330個點,都是已經標注好的了,我們把其中的320個點賦值為-1,這樣就可以假裝這320個點都是沒有標注的了
  • 訓練一個只有10個標記點的標簽傳播模型
  • 然后從所有數據中選擇要標記的前五個最不確定的點,把它們(帶有正確標簽)放到原來的10個點中
  • 接下來可以訓練15個標記點(原始10個 + 5個新點)
  • 重復這個過程四次,就可以使用30個標記好的點來訓練模型
  • 可以通過改變max_iterations將這個值增加到30以上

以上是sklearn的操作流程,大家可能會有點糊塗
實際任務應該是這樣的。假設我們有一份數據集,共330個數字,其中前十個是已知的,已經標注好了,后320個是未知的,需要我們預測出來的。

  • 首先把這330個數據全部都放到半監督學習算法里,訓練模型,預測那320個標簽
  • 然后用某種方法(看下面代碼的操作)得知這320個數據里最不確定的前5個數據,對它進行人工標注,然后把它放到之前的10個數據里,現在就有15個已知數據了
  • 這樣循環個幾次,已標注的數據就變多了,那么分類器的效果肯定也就變好了

1.導入各種數據包

import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
from sklearn import datasets
from sklearn.semi_supervised import label_propagation
from sklearn.metrics import classification_report,confusion_matrix

# 再加下面這個,不然會報錯
from scipy.sparse.csgraph import *

2.讀取數據集

digits = datasets.load_digits()
rng = np.random.RandomState(0)
# indices是隨機產生的0-1796個數字,且打亂
indices = np.arange(len(digits.data))
rng.shuffle(indices)

# 取前330個數字來玩
X = digits.data[indices[:330]]
y = digits.target[indices[:330]]
images = digits.images[indices[:330]]

n_total_samples = len(y) # 330
n_labeled_points = 10 # 標注好的數據共10條
max_iterations = 5 # 迭代5次

unlabeled_indices = np.arange(n_total_samples)[n_labeled_points:] # 未標注的數據320條
f = plt.figure() # 畫圖用的

3. 訓練模型且畫圖

建議大家把自己不懂的地方打印出來看看是啥意思,比如下面

for i in range(max_iterations):
    if len(unlabeled_indices) == 0:
        print("no unlabeled items left to label") # 沒有未標記的標簽了,全部標注好了
        break
    y_train = np.copy(y)
    y_train[unlabeled_indices] = -1 #把未標注的數據全部標記為-1,也就是后320條數據
    
    lp_model = label_propagation.LabelSpreading(gamma=0.25,max_iter=5) # 訓練模型
    lp_model.fit(X,y_train)
    
    predicted_labels = lp_model.transduction_[unlabeled_indices] # 預測的標簽
    true_labels = y[unlabeled_indices] # 真實的標簽
    
    cm = confusion_matrix(true_labels,predicted_labels,
                         labels = lp_model.classes_)
    
    print("預測標簽")
    print(predicted_labels)
    print("真實標簽")
    print(true_labels)
    print('----------------------------------------------')

經對比發現預測的標簽只有7個類,而非10個類

  • 原因就是我們一開始訓練的那10個數據只有7個類,所以預測其他320條數據的時候只能預測出這7個類
預測標簽
[2 8 6 6 6 6 1 9 5 8 8 2 8 7 7 6 7 9 2 9 7 7 6 8 9 1 8 1 9 1 1 6 7 7 9 9 7
 6 2 1 9 6 7 9 9 9 9 1 6 9 9 2 8 7 2 9 2 6 9 1 8 9 5 1 2 1 2 2 9 7 2 8 6 9
 9 8 7 5 1 2 9 9 8 1 7 7 1 1 6 1 5 9 2 6 8 9 2 1 7 7 9 7 8 9 7 5 8 2 1 9 2
 9 8 1 1 7 9 6 1 5 8 9 9 6 9 9 5 7 9 6 2 8 6 9 6 1 5 1 5 9 9 1 8 9 6 1 8 9
 1 7 6 7 6 5 6 9 8 8 9 8 6 1 9 7 2 6 8 8 6 7 1 9 6 9 9 8 9 8 9 7 7 9 7 8 9
 7 8 9 6 7 5 9 1 7 6 1 9 8 9 9 9 9 2 1 1 2 1 1 1 9 2 1 9 8 7 6 1 8 8 1 6 9
 9 6 9 2 2 9 7 6 1 1 9 7 2 7 8 6 6 7 5 2 8 7 2 7 9 5 7 9 9 2 6 5 9 7 1 8 8
 9 8 6 7 6 9 2 6 1 8 8 1 6 7 5 2 1 5 8 2 1 6 9 1 5 7 9 1 6 2 9 9 1 2 2 9 9
 6 9 7 2 9 7 5 8 6 7 8 2 8 7 9 7 2 6 5 1 5 1 9 8]
真實標簽
[2 8 6 6 6 6 1 0 5 8 8 7 8 4 7 5 4 9 2 9 4 7 6 8 9 4 3 1 0 1 8 6 7 7 1 0 7
 6 2 1 9 6 7 9 0 0 5 1 6 3 0 2 3 4 1 9 2 6 9 1 8 3 5 1 2 8 2 2 9 7 2 3 6 0
 5 3 7 5 1 2 9 9 3 1 7 7 4 8 5 8 5 5 2 5 9 0 7 1 4 7 3 4 8 9 7 9 8 2 6 5 2
 5 8 4 8 7 0 6 1 5 9 9 9 5 9 9 5 7 5 6 2 8 6 9 6 1 5 1 5 9 9 1 5 3 6 1 8 9
 8 7 6 7 6 5 6 0 8 8 9 8 6 1 0 4 1 6 3 8 6 7 4 5 6 3 0 3 3 3 0 7 7 5 7 8 0
 7 8 9 6 4 5 0 1 4 6 4 3 3 0 9 5 9 2 1 4 2 1 6 8 9 2 4 9 3 7 6 2 3 3 1 6 9
 3 6 3 2 2 0 7 6 1 1 9 7 2 7 8 5 5 7 5 2 3 7 2 7 5 5 7 0 9 1 6 5 9 7 4 3 8
 0 3 6 4 6 3 2 6 8 8 8 4 6 7 5 2 4 5 3 2 4 6 9 4 5 4 3 4 6 2 9 0 1 7 2 0 9
 6 0 4 2 0 7 9 8 5 4 8 2 8 4 3 7 2 6 9 1 5 1 0 8]
----------------------------------------------

3.1 完整代碼

for i in range(max_iterations):
    if len(unlabeled_indices) == 0:
        print("no unlabeled items left to label") # 沒有未標記的標簽了,全部標注好了
        break
    y_train = np.copy(y)
    y_train[unlabeled_indices] = -1 #把未標注的數據全部標記為-1,也就是后320條數據
    
    lp_model = label_propagation.LabelSpreading(gamma=0.25,max_iter=5) # 訓練模型
    lp_model.fit(X,y_train)
    
    predicted_labels = lp_model.transduction_[unlabeled_indices] # 預測的標簽
    true_labels = y[unlabeled_indices] # 真實的標簽
    
    cm = confusion_matrix(true_labels,predicted_labels,
                         labels = lp_model.classes_)
    
    print("iteration %i %s" % (i,70 * "_")) # 打印迭代次數
    print("Label Spreading model: %d labeled & %d unlabeled (%d total)"
         % (n_labeled_points,n_total_samples-n_labeled_points,n_total_samples))
    
    print(classification_report(true_labels,predicted_labels))
    
    print("Confusion matrix")
    print(cm)
    
    # 計算轉換標簽分布的熵
    # lp_model.label_distributions_作用是Categorical distribution for each item
    pred_entropies = stats.distributions.entropy(
    lp_model.label_distributions_.T)
    
    # 選擇分類器最不確定的前5位數字的索引
    # 首先計算出所有的熵,也就是不確定性,然后從320個中選擇出前5個熵最大的
    # numpy.argsort(A)提取排序后各元素在原來數組中的索引。具體情況可看下面
    #  np.in1d 用於測試一個數組中的值在另一個數組中的成員資格,返回一個布爾型數組。具體情況可看下面
    uncertainty_index = np.argsort(pred_entropies)[::1]
    uncertainty_index = uncertainty_index[
        np.in1d(uncertainty_index,unlabeled_indices)][:5] # 這邊可以確定每次選前幾個作為不確定的數,最終都會加回到訓練集
    
    # 跟蹤我們獲得標簽的索引
    delete_indices = np.array([])
    
    # 可視化前5次的結果
    if i < 5:
        f.text(.05,(1 - (i + 1) * .183),
              'model %d\n\nfit with\n%d labels' %
              ((i + 1),i*5+10),size=10)
    for index,image_index in enumerate(uncertainty_index):
        # image_index是前5個不確定標簽
        # index就是0-4
        image = images[image_index]

        # 可視化前5次的結果
        if i < 5:
            sub = f.add_subplot(5,5,index + 1 + (5*i))
            sub.imshow(image,cmap=plt.cm.gray_r)
            sub.set_title("predict:%i\ntrue: %i" % (
                lp_model.transduction_[image_index],y[image_index]),size=10)
            sub.axis('off')
        
        # 從320條里刪除要那5個不確定的點
        # np.where里面的參數是條件,返回的是滿足條件的索引
        delete_index, = np.where(unlabeled_indices == image_index)
        delete_indices = np.concatenate((delete_indices,delete_index))
        
    unlabeled_indices = np.delete(unlabeled_indices,delete_indices)
    # n_labeled_points是前面不確定的點有多少個被標注了
    n_labeled_points += len(uncertainty_index)
    
f.suptitle("Active learning with label propagation.\nRows show 5 most"
          "uncertain labels to learn with the next model")
plt.subplots_adjust(0.12,0.03,0.9,0.8,0.2,0.45)
plt.show()

3.2 numpy.argsort()函數

  • 提取排序后各元素在原來數組中的索引
import numpy as np
B=np.array([[4,2,3,55],[5,6,37,8],[-7,68,9,0]])
print('B:')
print(B)

print('')
print('默認輸出')
print(np.argsort(B))#默認的輸出每行元素的索引值。這些索引值對應的元素是從小到大排序的。

看打印的結果

B:
[[ 4  2  3 55]
 [ 5  6 37  8]
 [-7 68  9  0]]

默認輸出
[[1 2 0 3]
 [0 1 3 2]
 [0 3 2 1]]

3.3 np.in1d() 函數

  • 用於測試一個數組中的值在另一個數組中的成員資格,返回一個布爾型數組
values = np.array([6, 0, 0, 3, 2, 5, 6])
np.in1d(values, [2, 3, 6])

看打印的結果

array([ True, False, False,  True,  True, False,  True])

三. 總結

這次主要是想用半監督學習算法做NLP文本分類,看到sklearn庫里正好有這個算法包,想拿來試一下,結果跑不了那么大的數據量,算是失敗了。但是我覺得還是從中了解了很多,后面會寫一篇關於它的博客,里面關於文本的處理讓我學到了很多,走了很多的彎路。接下來我還會繼續探索怎么用少標注的數據來做文本分類。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM