餘弦計算相似度度量
相似度度量(Similarity),即計算個體間的相似程度,相似度度量的值越小,說明個體間相似度越小,相似度的值越大說明個體差異越大。
文本相似度計算的處理流程是:
(1)找出兩篇文章的關鍵詞;
(2)每篇文章各取出若干個關鍵詞,合併成一個集合,計算每篇文章對於這個集合中的詞的詞頻
(3)生成兩篇文章各自的詞頻向量;
(4)計算兩個向量的餘弦相似度,值越大就表示越相似。
TF-IDF(Term Frequency-Inverse Document Frequency, 詞頻-逆文件頻率)(字面)
是一種用於資訊檢索與資訊探勘的常用加權技術。TF-IDF是一種統計方法,用以評估一字詞對於一個文件集或一個語料庫中的其中一份文件的重要程度。字詞的重要性隨着它在文件中出現的次數成正比增加,但同時會隨着它在語料庫中出現的頻率成反比下降。(一個詞在文章中出現次數越多,同時在所有文檔中出現次數越少, 越能夠代表該文章,越能反應文章的特性,是我們所需要的關鍵詞)
詞頻(TF):指一個詞在文章中出現的次數
逆文檔頻率(IDF):IDF的主要思想是:如果包含詞條t的文檔越少, IDF越大,則說明詞條具有很好的類別區分能力。某一特定詞語的IDF,可以由總文件數目除以包含該詞語之文件的數目,再將得到的商取對數得到。
停用詞(word stop):出現次數最多的是“的”,“是”,“在”,“我”等等,這類詞對結果毫無幫助,必須過濾掉的詞
將TF和IDF進行相乘,得到一個詞的TF-IDF值,某個詞對文章重要性越高,該值越大,於是排在前面幾個詞,就是這篇文章的關鍵詞TF
TF-IDF與一個詞在文檔中的出現次數成正比,與包含該詞的文檔數成反比
實踐:
有500多篇文章,利用MapReduce實現TF-IDF值
首先這是508篇文章
其中一篇文章內容,算文本相似度的前提需要對其進行中文分詞
由於文件多,所以需要將其整合到一個大的文件,變成一行是一篇文章,將508篇獨立文章變成508行。編輯convert.py進行整合
convert.py
import os
import sys
file_path_dir = sys.argv[1]
def read_file_handler(f):
fd = open(f, 'r')
return fd
file_name = 0
for fd in os.listdir(file_path_dir):
file_path = file_path_dir + '/' + fd
content_list = []
file_fd = read_file_handler(file_path)
for line in file_fd:
content_list.append(line.strip())
print '\t'.join([str(file_name), ' '.join(content_list)])
file_name += 1
整合後的效果,以兩篇爲例(idf_input.data)
run.sh,通過MR框架算出IDF
HADOOP_CMD="/usr/local/src/hadoop-2.6.1/bin/hadoop"
STREAM_JAR_PATH="/usr/local/src/hadoop-2.6.1//share/hadoop/tools/lib/hadoop-streaming-2.6.1.jar"
INPUT_FILE_PATH="/idf_input.data"
OUTPUT_PATH="/tfidf_output"
$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_PATH
# Step 1.
$HADOOP_CMD jar $STREAM_JAR_PATH \
-input $INPUT_FILE_PATH \
-output $OUTPUT_PATH \
-mapper "python map.py" \
-reducer "python red.py" \
-file ./map.py \
-file ./red.py
map.py,將整合完的文件通過MR框架進行標準輸入,根據“\t”分割,然後判斷將一些髒數據去掉。分成一個文件名和一個文件內容,根據IDF公式我們需要知道某個詞在哪幾篇文章中包括,然後在map函數中需要同一片文章中某個詞重複出現進行去重,所以將數組word_list利用set()函數進行去重,然後循環遍歷打印
import sys
for line in sys.stdin:
ss = line.strip().split('\t')
if len(ss) != 2:
continue
file_name, file_content = ss
word_list = file_content.strip().split(' ')
word_set = set(word_list)
for word in word_set:
print '\t'.join([word, '1'])
red.py,根據map的輸出,然後傳入red中對其"\t"分割,去髒數據,循環算出idf
import sys
import math
current_word = None
sum = 0
docs_cnt = 508
for line in sys.stdin:
ss = line.strip().split('\t')
if len(ss) != 2:
continue
word, val = ss
if current_word == None:
current_word = word
if current_word != word:
idf = math.log(float(docs_cnt) / (float(sum) + 1.0))
print '\t'.join([current_word, str(idf)])
current_word = word
sum = 0
sum += int(val)
idf = math.log(float(docs_cnt) / (float(sum) + 1.0))
print '\t'.join([current_word, str(idf)])
通過MR跑出來的數據儲存在HDFS中,將結果下載到本地part-00000
然後算TF-IDF,單機執行,傳入part-00000 idf_input.data 文件名 某個詞
tfidf.demo.py,首先對part-00000進行“\t”分割,切割成token,idf,保存到字典idf_dict中;然後對idf_input.data進行“\t”分割,分割成文件名,content,對content進行“ ”分割,循環遍歷分割後的數組,如果不在字典tf_dict中,那麼添加到字典中,若存在則tf_dict[token] +=1。當輸入的token不在這兩個字典中,就退出,否則進行tf-idf計算。
import sys
input_idf_dict_path = sys.argv[1]
input_docs_path = sys.argv[2]
input_docid = sys.argv[3]
input_token = sys.argv[4]
idf_dict = {}
with open(input_idf_dict_path,'r') as fd:
for line in fd:
ss = line.strip().split('\t')
if len(ss) != 2:
continue
token,idf=ss
idf_dict[token] = idf
tf_dict = {}
content_size = 0
with open(input_docs_path,'r') as fd:
for line in fd:
ss = line.strip().split('\t')
if len(ss) !=2:
continue
docid,content = ss
if docid != input_docid:
continue
for token in content.strip().split(' '):
if token not in tf_dict:
tf_dict[token] = 1
else:
tf_dict[token] += 1
content_size += 1-
if input_token not in idf_dict or input_token not in tf_dict:
print 'no found token'
sys.exit(-1)
tfidf = float(idf_dict[input_token]) * float(tf_dict[input_token]) / float(content_size)
print '\t'.join([input_docid,input_token,str(tfidf)])
查詢第9篇裏的酷派