1. 程式人生 > 其它 >怎樣做中文文字的情感分析?

怎樣做中文文字的情感分析?

2016課程地址 專案描述地址


什麼是情感分析?

就是要識別出使用者對一件事一個物或一個人的看法、態度,比如一個電影的評論,一個商品的評價,一次體驗的感想等等。根據對帶有情感色彩的主觀性文字進行分析,識別出使用者的態度,是喜歡,討厭,還是中立。在實際生活中有很多應用,例如通過對 Twitter 使用者的情感分析,來預測股票走勢、預測電影票房、選舉結果等,還可以用來了解使用者對公司、產品的喜好,分析結果可以被用來改善產品和服務,還可以發現競爭對手的優劣勢等等。

怎麼分析,技術上如何實現?

首先這是個分類問題。

最開始的方案是在文中找到具有各種感情色彩屬性的詞,統計每個屬性的詞的個數,哪個類多,這段話就屬於哪個屬性。但是這存在一個問題,例如 don't like ,一個屬於否定,一個屬於肯定,統計之後變成 0 了,而實際上應該是否定的態度。再有一種情況是,前面幾句是否定,後面又是肯定,那整段到底是中立還是肯定呢,為了解決這樣的問題,就需要考慮上下文的環境。

2013年穀歌發了兩篇論文,介紹了 Continuous Bag of Words (CBOW) 和 Skip-gram 這兩個模型,也就是 Word2Vec 方法,這兩種模型都是先將每個單詞轉化成一個隨機的 N 維向量,訓練之後得到每個單詞的最優表示向量,區別是,CBOW 是根據上下文來預測當前詞語,Skip-gram 剛好相反,是根據當前詞語來預測上下文。

Word2Vec 方法不僅可以捕捉上下文語境,同時還壓縮了資料規模,讓訓練更快更高效。通過這個模型得到的詞向量已經可以捕捉到上下文的資訊。比如,可以利用基本代數公式來發現單詞之間的關係(比如,“國王”-“男人”+“女人”=“王后”)。用這些自帶上下文資訊的詞向量來預測未知資料的情感狀況的話,就可以更準確。

今天的小專案,就是用 word2vec 去解決情感分析問題的。先來簡單介紹一下大體思路,然後進入程式碼版塊。

思路分為兩部分,第一步,就是先用 word2vec 和 SGD 訓練出每個單詞的最優表示向量。第二步,用 Softmax Regression 對訓練資料集的每個句子進行訓練,得到分類器的引數,用這個引數就可以預測新的資料集的情感分類。其中訓練資料集的每個句子,都對應一個0-1之間的浮點得分,將這個得分化為 0-4 整數型 5 個級別,分別屬於 5 種感情類別,討厭,有點討厭,中立,有點喜歡,喜歡。然後將每個句子的詞轉化成之前訓練過的詞向量,這樣哪些詞屬於哪個類就知道了,然後用分類器得到分類的邊界,得到的引數就可以用來進行預測。

具體實現

接下來以一個初學者的角度來講一下要如何利用這幾個模型和演算法來實現情感分析這個任務的,因為專案的程式碼有點多,不方便全寫在文章裡。可以回覆公眾號“情感”獲取原始碼下載地址。

第一步,用 word2vec 和 SGD 訓練出每個單詞的最優表示向量。

  • 執行 c7_run_word2vec.py
  • 其中訓練詞向量的方法是 c5_word2vec.py
  • 同時用 c6_sgd.py 訓練引數,並且將結果儲存起來,每1000次迭代儲存在一個檔案中 saved_params_1000.npy

word2vec: 上面提到了,它有兩種模型 CBOW 和 Skip-gram,每一種都可以用來訓練生成最優的詞向量,同時還有兩種 cost function 的定義方式,一種是 Softmax cost function, 一種是 Negative sampling cost function,所以在提到 word2vec 的時候,其實是可以有 4 種搭配的方法的,這個小專案裡用到的是 Skip-gram 和 Negative sampling cost function 的結合方式。

先定義 skipgram 函式: 給一箇中心詞 currentWord,和它的視窗大小為 2C 的上下文 contextWords,要求出代表它們的詞向量矩陣 W1 和 W2。

def skipgram(currentWord, C, contextWords, tokens, inputVectors, outputVectors, dataset, word2vecCostAndGradient = softmaxCostAndGradient): """ Skip-gram model in word2vec """ currentI = tokens[currentWord] #the order of this center word in the whole vocabulary predicted = inputVectors[currentI, :] #turn this word to vector representation cost = 0.0 gradIn = np.zeros(inputVectors.shape) gradOut = np.zeros(outputVectors.shape) for cwd in contextWords: #contextWords is of 2C length idx = tokens[cwd] cc, gp, gg = word2vecCostAndGradient(predicted, idx, outputVectors, dataset) cost += cc #final cost/gradient is the 'sum' of result calculated by each word in context gradOut += gg gradIn[currentI, :] += gp return cost, gradIn, gradOut

這裡用到的成本函式是 Negative sampling,我們的目的就是要使這個成本函式達到最小,然後用這個極值時的引數 grad, 也就是可以得到要求的 wordvectors。要增加準確度,所以可以多次生成中心詞和上下文進行訓練,然後取平均值,也就是函式 word2vec_sgd_wrapper 做的事情。

def negSamplingCostAndGradient(predicted, target, outputVectors, dataset, K=10):    """ Negative sampling cost function for word2vec models """

    grad = np.zeros(outputVectors.shape)
    gradPred = np.zeros(predicted.shape)    indices = [target]    for k in xrange(K):
        newidx = dataset.sampleTokenIdx()        while newidx == target:
            newidx = dataset.sampleTokenIdx()        indices += [newidx]    labels = np.array([1] + [-1 for k in xrange(K)])
    vecs = outputVectors[indices, :]

    t = sigmoid(vecs.dot(predicted) * labels)
    cost = -np.sum(np.log(t))    delta = labels * (t-1)
    gradPred = delta.reshape((1, K+1)).dot(vecs).flatten()
    gradtemp = delta.reshape((K+1, 1)).dot(predicted.reshape(1, predicted.shape[0]))    for k in xrange(K+1):
        grad[indices[k]] += gradtemp[k, :]    return cost, gradPred, grad

接著用 sgd 迭代 40000 次得到訓練好的 wordVectors。

wordVectors0 = sgd(    lambda vec: word2vec_sgd_wrapper(skipgram, tokens, vec, dataset, C,
        negSamplingCostAndGradient),
        wordVectors, 0.3, 40000, None, True, PRINT_EVERY=10)

第二步,用 Softmax Regression 對訓練資料集進行分類學習。

  • 執行 c10_sentiment.py
  • 其中用 c6_sgd.py 去訓練權重 weights,
  • 然後用 c8_softmaxreg.py 根據訓練好的 features,labels,weights 進行類別 label 的預測。

先將資料集分為三部分,training set,deviation set,和 test set。

trainset = dataset.getTrainSentences()
devset = dataset.getDevSentences()testset = dataset.getTestSentences()

在 trainset 中,每句話對應一個情感的得分或者說是分類,先將每個 word 在 token 中找到序號,然後在第一步訓練好的 wordvectors 中找到相應的詞向量。

trainFeatures[i, :] = getSentenceFeature(tokens, wordVectors, words)

然後用 sgd 和 softmax_wrapper 迭代 10000 次去訓練 weights:

weights = sgd(lambda weights: 
softmax_wrapper(trainFeatures, trainLabels, weights, regularization), 
weights, 3.0, 10000, PRINT_EVERY=100)

接著用 softmax regression 進行分類的預測:

 _, _, pred = softmaxRegression(trainFeatures, trainLabels, weights)

上面用到了不同的 REGULARIZATION=[0.0, 0.00001, 0.00003, 0.0001, 0.0003, 0.001, 0.003, 0.01] ,在其中選擇 accuracy 最好的 REGULARIZATION 和相應的結果

best_dev = 0for result in results:    if result["dev"] > best_dev:
        best_dev = result["dev"]
        BEST_REGULARIZATION = result["reg"]
        BEST_WEIGHTS = result["weights"]

用這個最好的引數在 test set 上進行預測:

_, _, pred = softmaxRegression(testFeatures, testLabels, BEST_WEIGHTS)

並且的到 accuracy:

print "Test accuracy (%%): %f" % accuracy(testLabels, pred)

下圖是 accuracy 和 REGULARIZATION 在 devset 和 trainset 上的趨勢:

accuracy


以上就是 sentiment analysis 的基本實現,把它和爬蟲相結合,會有很多好玩的玩兒法!