1. 介紹
余弦相似度(Cosine Similarity),又稱為余弦相似性,是通過計算兩個向量的夾角余弦值來評估他們的相似度。余弦相似度僅僅與向量的指向方向相關,與向量的長度無關,它將向量根據(jù)坐標值繪制到向量空間中,如最常見的二維空間。因此,萬物皆向量,我們可以使用余弦相似度來進行相似圖片查找、相似文件搜索等工作。
兩個向量間的余弦值可以通過使用歐幾里得點積公式求出:
給定兩個屬性向量,A 和 B,其余弦相似性 θ 由點積和向量長度給出,如下所示:
原理
余弦相似度將向量根據(jù)坐標值,繪制到向量空間中,如最常見的二維空間。
- 當兩個向量的夾角為0°,即余弦值為1,則兩個向量有相同的指向,相當于相似度最高(其他任何角度的余弦值都不大于1)。
- 當兩個向量的夾角為90°,即余弦值為0,則兩個向量垂直。
- 當兩個向量的夾角為180°,即余弦值為-1,則兩個向量指向完全相反的方向,相當于完全不是同類。
余弦相似度而非算法,求出余弦相似度后,到底閾值如何界定(值大于多少認為是樣本的相似同類),往往需要依次用不同的閾值數(shù)值對全部數(shù)據(jù)集進行測試,挑選效果最好的數(shù)值作為閾值。
余弦相似度通常用于正空間。余弦值的范圍在-1到1之間,因為包含負值,有時不便于使用。改進方法有:
- 將余弦相似度用于正空間,對于各個維度均為正的向量,可以保證余弦相似度非負(該空間的夾角被限定在 0-90,或者根據(jù)公式,內積恒為正),所以可以轉為 [0, 1] 上的有界相似性。
- 用1減余弦相似度,此時結果范圍為 [0, 2],且值越小表示越接近(類似歐氏距離)。
?
2. 實驗一:查找相似圖像
2.1.1 魔法
- 圖像加載和預處理: 讀取目標圖像。預處理圖像,例如轉換為灰度圖像、調整大小等。
- 特征提?。?/strong> 選擇圖像特征,這里通常使用直方圖,對每張圖像計算所選特征,得到特征向量。
- 相似度計算: 使用余弦相似度計算兩個特征向量之間的相似性。相似度的計算通常在 [0, 1] 范圍,越接近1表示越相似。
- 排序和篩選: 對相似圖像按照相似度降序排序。根據(jù)需求,可以選擇保留相似度高于某個閾值的圖像。
- 結果展示: 展示相似度高的圖像作為結果。可以通過圖形界面、命令行輸出或其他方式呈現(xiàn)結果。
2.1.2 實驗
第一步:圖像加載和預處理
讀取目標圖像。預處理圖像,例如轉換為灰度圖像、調整大小等。
"""
以圖搜圖:余弦相似度(Cosine Similarity)查找相似圖像的原理與實現(xiàn)
實驗環(huán)境:Win10 | python 3.9.13 | OpenCV 4.4.0 | numpy 1.21.1 | Matplotlib 3.7.1
實驗時間:2023-11-30
實例名稱:imgCosineSimilarity_v1.0_show.py
"""
import os
import cv2
import matplotlib.pyplot as plt
# 目標圖像素材庫文件夾路徑
database_dir = '../../P0_Doc/img_data/'
# 讀取查詢圖像和數(shù)據(jù)庫中的圖像
img1_path = database_dir + 'car-101.jpg'
img2_path = database_dir + 'car-102.jpg'
img3_path = database_dir + 'car-103.jpg'
img4_path = database_dir + 'car-106.jpg'
img5_path = database_dir + 'car-109.jpg'
# 讀取圖像
img1 = cv2.imread(img1_path)
img2 = cv2.imread(img2_path)
img3 = cv2.imread(img3_path)
img4 = cv2.imread(img4_path)
img5 = cv2.imread(img5_path)
# 將圖像轉換為灰度圖像
img1_gray = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
img2_gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
img3_gray = cv2.cvtColor(img3, cv2.COLOR_BGR2GRAY)
img4_gray = cv2.cvtColor(img4, cv2.COLOR_BGR2GRAY)
img5_gray = cv2.cvtColor(img5, cv2.COLOR_BGR2GRAY)
# 繪制子圖
plt.figure(figsize=(12, 4))
# 繪制灰度圖像
plt.subplot(1, 5, 1)
plt.imshow(img1_gray, cmap='gray')
plt.title(os.path.basename(img1_path))
plt.subplot(1, 5, 2)
plt.imshow(img2_gray, cmap='gray')
plt.title(os.path.basename(img2_path))
plt.subplot(1, 5, 3)
plt.imshow(img3_gray, cmap='gray')
plt.title(os.path.basename(img3_path))
plt.subplot(1, 5, 4)
plt.imshow(img4_gray, cmap='gray')
plt.title(os.path.basename(img4_path))
plt.subplot(1, 5, 5)
plt.imshow(img5_gray, cmap='gray')
plt.title(os.path.basename(img5_path))
plt.tight_layout()
# 顯示灰度圖像
plt.show()
灰度圖像:
第二步:特征提取
選擇圖像特征,這里通常使用直方圖,對每張圖像計算所選特征,得到特征向量。
# 計算圖像的直方圖
img1_hist = cv2.calcHist([img1_gray], [0], None, [256], [0, 256])
img2_hist = cv2.calcHist([img2_gray], [0], None, [256], [0, 256])
img3_hist = cv2.calcHist([img3_gray], [0], None, [256], [0, 256])
img4_hist = cv2.calcHist([img4_gray], [0], None, [256], [0, 256])
img5_hist = cv2.calcHist([img5_gray], [0], None, [256], [0, 256])
# 獲取圖像的特征向量
vector1 = img1_hist.flatten()
vector2 = img2_hist.flatten()
vector3 = img3_hist.flatten()
vector4 = img4_hist.flatten()
vector5 = img5_hist.flatten()
# 使用垂直線(stem lines)繪制向量
plt.figure(figsize=(8, 4))
# 繪制向量1
plt.subplot(1, 5, 1)
plt.stem(vector1)
plt.title('Vector 1')
# 繪制向量2
plt.subplot(1, 5, 2)
plt.stem(vector2)
plt.title('Vector 2')
# 繪制向量3
plt.subplot(1, 5, 3)
plt.stem(vector3)
plt.title('Vector 3')
# 繪制向量4
plt.subplot(1, 5, 4)
plt.stem(vector4)
plt.title('Vector 4')
# 繪制向量5
plt.subplot(1, 5, 5)
plt.stem(vector5)
plt.title('Vector 5')
# 圖像向量可視化
plt.tight_layout()
plt.show()
圖像特征向量可視化,橫向對比:
使用散點圖繪制特征向量:
# 使用散點圖繪制向量
plt.figure(figsize=(8, 4))
# 繪制散點圖
plt.scatter(range(len(vector1)), vector1, label='Vector 1', marker='o', s=10)
plt.scatter(range(len(vector2)), vector2, label='Vector 2', marker='x', s=10)
plt.scatter(range(len(vector3)), vector3, label='Vector 3', marker='o', s=10)
plt.scatter(range(len(vector4)), vector4, label='Vector 4', marker='o', s=10)
plt.scatter(range(len(vector5)), vector5, label='Vector 5', marker='o', s=10)
plt.title('Scatter Plot of Vectors')
plt.xlabel('Index')
plt.ylabel('Value')
# 添加圖例
plt.legend()
# 圖像向量可視化
plt.show()
圖像特征向量散點圖可視化:
通過可視化縱向對比測試圖像的特征向量,不難發(fā)現(xiàn),圖像1與圖像2的特征向量完全重合,即完全相似。
第三步:相似度計算
使用余弦相似度計算兩個特征向量之間的相似性。相似度的計算通常在 [0, 1] 范圍,越接近1表示越相似。
# 歸一化直方圖:將特征表示成一維向量
vector1 = img1_hist.flatten()
vector2 = img2_hist.flatten()
# 計算向量 vector1 和 vector2 的點積,即對應元素相乘后相加得到的標量值
dot_product = np.dot(vector1, vector2)
# 計算向量 vector1 的 L2 范數(shù),即向量各元素平方和的平方根
norm_vector1 = np.linalg.norm(vector1)
# 計算向量 vector2 的 L2 范數(shù)
norm_vector2 = np.linalg.norm(vector2)
# 利用余弦相似度公式計算相似度,即兩個向量的點積除以它們的 L2 范數(shù)之積
similarity = dot_product / (norm_vector1 * norm_vector2)
print(f"圖像名稱:{img2_path},與目標圖像 {img1_path} 的近似值:{similarity}")
輸出打印:
圖像名稱:img_test/car-102.jpg,與目標圖像 img_test/car-101.jpg 的近似值:1.0
第四步:排序和篩選
對相似圖像按照相似度降序排序。根據(jù)需求,可以選擇保留相似度高于某個閾值的圖像。
if (similarity > 0.8):
print(f"圖像名稱:{img2_path},與目標圖像 {img1_path} 的近似值:{similarity}")
或者,如下為多圖相似實驗部分代碼(完整代碼可參見下文 2.1.3 實驗代碼):
def image_search(query_path, database_paths):
# 提取查詢圖像的特征
query_feature = extract_features(query_path)
# 遍歷數(shù)據(jù)庫圖像并比較相似度
similaritys = []
for database_path in database_paths:
# 提取數(shù)據(jù)庫圖像的特征
database_feature = extract_features(database_path)
# 計算余弦相似度
similarity = cosine_similarity(query_feature, database_feature)
# 將結果保存到列表中
if (similarity > 0.8):
similaritys.append((database_path, similarity))
# 按相似度降序排序
similaritys.sort(key=lambda x: x[1], reverse=True)
return similaritys
第五步:結果展示
展示相似度高的圖像作為結果??梢酝ㄟ^圖形界面、命令行輸出或其他方式呈現(xiàn)結果。
具體可見如下測試代碼。
?
2.1.3 測試
實驗場景
通過 opencv,使用余弦相似度查找目標圖像素材庫中所有相似圖像,要求相似值大于等于 0.65(余弦相似度通常在 [-1, 1] 范圍,越接近1表示越相似)。
實驗素材
實驗代碼
"""
以圖搜圖:余弦相似度(Cosine Similarity)查找相似圖像的原理與實現(xiàn)
實驗環(huán)境:Win10 | python 3.9.13 | OpenCV 4.4.0 | numpy 1.21.1 | Matplotlib 3.7.1
實驗時間:2023-11-30
實例名稱:imgCosineSimilarity_v1.2.py
"""
import os
import time
import cv2
import numpy as np
import matplotlib.pyplot as plt
def extract_features(image_path):
# 讀取圖像并將其轉換為灰度
image = cv2.imread(image_path, cv2.COLOR_BGR2GRAY)
# 計算直方圖
hist = cv2.calcHist([image], [0], None, [256], [0, 256])
# 歸一化直方圖
# cv2.normalize(hist, hist): 這一步是將直方圖進行歸一化,確保其數(shù)值范圍在 [0, 1] 之間。歸一化是為了消除圖像的大小或強度的差異,使得直方圖更具有通用性
# .flatten(): 這一步將歸一化后的直方圖展平成一維數(shù)組。在余弦相似度計算中,我們需要將特征表示成一維向量,以便進行向量之間的相似度比較
hist = cv2.normalize(hist, hist).flatten()
return hist
def cosine_similarity(vector1, vector2):
# 算向量 vector1 和 vector2 的點積,即對應元素相乘后相加得到的標量值
dot_product = np.dot(vector1, vector2)
# 計算向量 vector1 的 L2 范數(shù),即向量各元素平方和的平方根
norm_vector1 = np.linalg.norm(vector1)
# 計算向量 vector2 的 L2 范數(shù)
norm_vector2 = np.linalg.norm(vector2)
# 利用余弦相似度公式計算相似度,即兩個向量的點積除以它們的 L2 范數(shù)之積
similarity = dot_product / (norm_vector1 * norm_vector2)
return similarity
def image_search(query_path, database_paths):
# 提取查詢圖像的特征
query_feature = extract_features(query_path)
# 遍歷數(shù)據(jù)庫圖像并比較相似度
similaritys = []
for database_path in database_paths:
# 提取數(shù)據(jù)庫圖像的特征
database_feature = extract_features(database_path)
# 計算余弦相似度
similarity = cosine_similarity(query_feature, database_feature)
# 將結果保存到列表中(僅保留相似值大于等于 0.8 的圖像)
if (similarity >= 0.65):
similaritys.append((database_path, similarity))
# 按相似度降序排序
similaritys.sort(key=lambda x: x[1], reverse=True)
return similaritys
def show_similar_images(similar_images, images_per_column=3):
# 計算總共的圖片數(shù)量
num_images = len(similar_images)
# 計算所需的行數(shù)
num_rows = (num_images + images_per_column - 1) // images_per_column
# 創(chuàng)建一個子圖,每行顯示 images_per_column 張圖片
fig, axes = plt.subplots(num_rows, images_per_column, figsize=(12, 15), squeeze=False)
# 遍歷每一行
for row in range(num_rows):
# 遍歷每一列
for col in range(images_per_column):
# 計算當前圖片在列表中的索引
index = row * images_per_column + col
# 檢查索引是否越界
if index < num_images:
# 獲取當前相似圖片的路徑和相似度
image_path = similar_images[index][0]
similarity = similar_images[index][1]
# 讀取圖片并轉換顏色通道
image = cv2.cvtColor(cv2.imread(image_path), cv2.COLOR_BGR2RGB)
# 在子圖中顯示圖片
axes[row, col].imshow(image)
# 設置子圖標題,包括圖片路徑和相似度
axes[row, col].set_title(f"Similar Image: {os.path.basename(image_path)} \n Similar Score: {similarity:.4f}")
# 關閉坐標軸
axes[row, col].axis('off')
# 顯示整個圖
plt.show()
if __name__ == "__main__":
time_start = time.time()
# 目標圖像素材庫文件夾路徑
database_folder_path = '../../P0_Doc/img_data/'
# 指定測試圖像文件擴展名
img_suffix = ['.jpg', '.jpeg', '.png', '.bmp', '.gif']
# 目標查詢圖像路徑
query_image_path = database_folder_path + 'apple-101.jpg'
query_image_path = database_folder_path + 'X3-01.jpg'
query_image_path = database_folder_path + 'Q3-01.jpg'
query_image_path = database_folder_path + 'car-101.jpg'
# 獲取目標圖像素材庫文件夾中所有圖像的路徑
all_files = [os.path.join(database_folder_path, filename) for filename in os.listdir(database_folder_path)]
# 篩選出指定后綴的圖像文件
img_files = [file for file in all_files if any(file.endswith(suffix) for suffix in img_suffix)]
# 進行相似圖像搜索
search_results = image_search(query_image_path, img_files)
# 打印結果
for similarity in search_results:
print(f"圖像名稱:{similarity[0]},與目標圖像 {os.path.basename(query_image_path)} 的近似值:{similarity[1]}")
time_end = time.time()
print(f"耗時:{time_end - time_start}")
# 顯示目標相似圖像
show_similar_images(search_results)
輸出打?。?/p>
圖像名稱:../../P0_Doc/img_data/car-101.jpg,與目標圖像 car-101.jpg 的近似值:1.0
圖像名稱:../../P0_Doc/img_data/car-102.jpg,與目標圖像 car-101.jpg 的近似值:1.0
圖像名稱:../../P0_Doc/img_data/car-103.jpg,與目標圖像 car-101.jpg 的近似值:0.8792840838432312
圖像名稱:../../P0_Doc/img_data/car-106.jpg,與目標圖像 car-101.jpg 的近似值:0.8591960668563843
圖像名稱:../../P0_Doc/img_data/car-109.jpg,與目標圖像 car-101.jpg 的近似值:0.8135514259338379
圖像名稱:../../P0_Doc/img_data/Q3-07.jpg,與目標圖像 car-101.jpg 的近似值:0.7921913266181946
圖像名稱:../../P0_Doc/img_data/car-104.jpg,與目標圖像 car-101.jpg 的近似值:0.7479972839355469
圖像名稱:../../P0_Doc/img_data/car-105.jpg,與目標圖像 car-101.jpg 的近似值:0.7401522397994995
圖像名稱:../../P0_Doc/img_data/X3-01.jpg,與目標圖像 car-101.jpg 的近似值:0.718971848487854
圖像名稱:../../P0_Doc/img_data/X3-10.jpg,與目標圖像 car-101.jpg 的近似值:0.718971848487854
圖像名稱:../../P0_Doc/img_data/X3-07.jpg,與目標圖像 car-101.jpg 的近似值:0.6954472661018372
圖像名稱:../../P0_Doc/img_data/Q3-11.jpg,與目標圖像 car-101.jpg 的近似值:0.6589514017105103
圖像名稱:../../P0_Doc/img_data/X3-05.jpg,與目標圖像 car-101.jpg 的近似值:0.6564251184463501
圖像名稱:../../P0_Doc/img_data/X3-02.jpg,與目標圖像 car-101.jpg 的近似值:0.6537510752677917
耗時:0.9245285987854004
多圖相似查找結果顯示:
?
2.1.4 實驗總結
經(jīng)過多組目標測試圖像的相似圖查找,對于旋轉、倒置的相似圖像查找非常準確。對于相似值在 0.8 ~ 0.65 之間的相似圖像查找效果差強人意。
優(yōu)點
- 簡單直觀:余弦相似度是一種簡單且直觀的相似性度量方法,易于理解和實現(xiàn)。
- 計算速度較快:在一些小規(guī)模的圖像數(shù)據(jù)庫中,余弦相似度的計算速度相對較快,適用于實時性要求不高的場景。
- 適用于高維度特征:余弦相似度對于高維度特征空間的相似性度量效果較好,適用于圖像的特征向量較長的情況。
缺點
- 不考慮空間結構:余弦相似度只關注特征向量的方向,而不考慮特征在空間中的分布結構。對于圖像中的空間信息,余弦相似度并未進行有效的建模。
- 不考慮像素間的相對位置:余弦相似度不考慮像素在圖像中的相對位置,對于圖像內容的排列順序不敏感,這在一些場景中可能并不符合實際需求。
- 對圖像噪聲敏感:如果圖像中存在噪聲,余弦相似度可能會受到噪聲的影響,導致相似度計算不準確。
- 不適用于大規(guī)模數(shù)據(jù)庫:在大規(guī)模圖像數(shù)據(jù)庫中,計算余弦相似度可能會變得相對較慢,不太適用于對實時性要求較高的場景。
?
2. 實驗二:查找相似文本
在相似文件查找場景中,余弦相似性將文件表示為向量,向量的每個維度代表文件的某個特征,比如文件的內容、詞頻、TF-IDF值等。然后,通過計算文件向量之間的余弦相似度,可以評估它們之間的相似程度。
由于一個詞的頻率不能為負數(shù),所以這兩個文件的余弦相似性范圍是從0到1。即,兩個詞的頻率向量之間的角度不能大于90°。
2.2.1 魔法
- 文件讀取和預處理: 讀取目標文本文件,對目標文本進行預處理,包括分詞、去停用詞、移除標點符號和數(shù)字等。
- 提取文件特征: 將目標文本表示為特征向量。這可以通過不同的方法,比如文本文件可以使用詞袋模型、TF-IDF等。
- 計算余弦相似度: 遍歷測試庫中的文本文件,對每個文件執(zhí)行相似度計算。
- 獲取相似文本: 根據(jù)需求設定一個閾值,將相似度大于閾值的文件視為相似文件,并按相似度結果排序,得到相似度最高的文本文件。
2.2.2 核心
- 分詞: 將一段文本切分成一個個有意義的詞語
- 構建詞匯表: 列出所有的詞,將所有文檔中出現(xiàn)的詞語構建為一個詞匯表,該詞匯表包含了所有可能的詞語
- 計算詞頻: 對應單詞在文本中出現(xiàn)的次數(shù)
- 詞頻向量化: 將文本表示為詞頻向量,以便計算文本之間的相似度
2.2.3 實驗
第一步:文件讀取和預處理
讀取目標文本文件,對目標文本進行預處理,包括分詞、去停用詞、移除標點符號和數(shù)字等。
"""
以圖搜圖:余弦相似度(Cosine Similarity)查找相似文本的原理與實現(xiàn)
實驗目的:文件讀取和預處理
實驗環(huán)境:Win10 | python 3.9.13 | OpenCV 4.4.0 | numpy 1.21.1 | Matplotlib 3.7.1 | jieba 0.42.1
實驗時間:2023-11-30
實例名稱:txtConsineSimilarity_v1.0.py
"""
import re
import jieba
# 預處理目標文本
def preprocess_text(text):
print(f"文本文件內容:{text}")
# 將文本轉換為小寫
text = text.lower()
print(f"將文本轉為小寫:{text}")
# 移除標點符號、數(shù)字和中文標點符號
text = re.sub(r'[^a-z\u4e00-\u9fa5\s]', '', text)
print(f"移除標點符號后:{text}")
# 使用 jieba 進行中文分詞
text_words = jieba.cut(text)
# 將分詞結果拼接成字符串
processed_text = ' '.join(text_words)
print(f"將分詞結果拼接成字符串:{processed_text}")
return processed_text
if __name__ == "__main__":
# 本地測試文本素材庫
test_dir_path = '../../P0_Doc/txt_data/'
# 本地測試文本素材路徑
origin_file = test_dir_path + 'CosineSimilarity_定義_org.txt'
# 讀取目標文本
with open(origin_file, 'r', encoding='utf-8') as file:
origin_text = file.read()
# 預處理目標文本
origin_context = preprocess_text(origin_text)
輸出打印:
文本文件原內容:余弦相似度(Cosine Similarity),是通過計算兩個向量的夾角余弦值來評估他們的相似度。
將文本轉為小寫:余弦相似度(cosine similarity),是通過計算兩個向量的夾角余弦值來評估他們的相似度。
移除標點符號后:余弦相似度cosine similarity是通過計算兩個向量的夾角余弦值來評估他們的相似度
將分詞結果拼接成字符串:余弦 相似 度 cosine similarity 是 通過 計算 兩個 向量 的 夾角 余弦 值來 評估 他們 的 相似 度
文本向量化的核心之一是文本分詞。分詞是將一段文本切分成一個個有意義的詞語或標記的過程。在文本處理中,分詞是一個關鍵的預處理步驟,因為它決定了最終文本向量的特征。
對于英文文本,一般可以使用空格進行簡單的分詞。而對于中文文本,由于漢字沒有空格,需要使用中文分詞工具進行切分。
比如測試案例中
文本文件原內容:
余弦相似度(Cosine Similarity),是通過計算兩個向量的夾角余弦值來評估他們的相似度。
將文本轉為小寫:
余弦相似度cosine similarity是通過計算兩個向量的夾角余弦值來評估他們的相似度
移除標點符號后:
余弦相似度cosine similarity是通過計算兩個向量的夾角余弦值來評估他們的相似度
分詞:
余弦 相似 度 cosine similarity 是 通過 計算 兩個 向量 的 夾角 余弦 值來 評估 他們 的 相似 度
常見的中文分詞工具包括 jieba、pkuseg、THULAC 等。其中,jieba 是一個簡單而強大的中文分詞工具,廣泛應用于中文文本處理任務。
?
為什么要移除標點符號和數(shù)字?
移除標點符號和數(shù)字是因為在某些文本相似度計算中,這些字符通常不包含太多語義信息,但會增加文本的復雜性。在文本預處理階段,通過移除這些字符,可以減小詞匯量,使得計算的文本向量更加簡潔,聚焦于包含主要語義的單詞。
例如,標點符號和數(shù)字通常不對文本的整體語義產(chǎn)生太大影響,而且在不同文本中的使用方式可能會有很大差異。如果保留這些字符,可能會導致文本表示中包含大量的噪聲,降低相似度計算的準確性。
當然,在一些特殊的應用場景中,保留標點符號和數(shù)字可能是有意義的,這取決于具體的文本相似度任務和需求。在本實驗中,簡單地移除了標點符號和數(shù)字,但實際應用中可以根據(jù)任務的要求進行定制化的預處理。
?
第二步:提取文件特征
將目標文本表示為特征向量。這可以通過不同的方法,比如文本文件可以使用詞袋模型、TF-IDF等。
def get_vectorizer(origin_context):
# 構建文本向量:使用詞袋模型表示文本,過濾停用詞
origin_vectorizer = CountVectorizer(stop_words='english')
# 使用 CountVectorizer 將原始文本 origin_context 轉換為詞袋模型的向量表示
origin_vector = origin_vectorizer.fit_transform([origin_context])
print(f"文本詞頻矩陣:\n{origin_vector}")
# 獲取特征單詞列表
feature_names = origin_vectorizer.get_feature_names_out()
print(f"文本特征單詞列表:\n{feature_names}")
print(f"文本詞頻向量:\n{origin_vector.toarray()}")
總體而言,CountVectorizer(stop_words='english')
的作用是將文本數(shù)據(jù)轉換為詞頻矩陣,同時忽略英語停用詞。這是文本挖掘和自然語言處理中常用的預處理步驟。
-
CountVectorizer 類:
CountVectorizer
是scikit-learn
中用于將文本數(shù)據(jù)轉換為詞頻(term frequency)矩陣的類。它將文本數(shù)據(jù)轉換為一個矩陣,其中每一行代表一個文本樣本,每一列代表一個不同的單詞,而矩陣的元素是對應單詞在文本樣本中出現(xiàn)的次數(shù)。 -
stop_words=‘english’ 參數(shù):
stop_words
參數(shù)用于指定停用詞(stop words)的處理方式。停用詞是在文本分析中通常被忽略的常見詞匯,因為它們通常不包含有用的信息。在這里,‘english’ 表示使用英語停用詞列表,這些詞會在文本向量化時被忽略 -
fit_transform 的過程:
- 構建詞匯表:詞匯表是所有文本中出現(xiàn)的獨特單詞的集合。
- 將文本轉換為詞頻矩陣:對于每個文本,統(tǒng)計詞匯表中每個單詞的出現(xiàn)次數(shù),將其轉換為向量表示。
最終,origin_vector 是一個稀疏矩陣,其中每一行對應于一個單詞,每一列對應于原始文本中對應單詞的出現(xiàn)次數(shù)。
輸出打印:
文本詞頻矩陣:
(0, 4) 2
(0, 8) 2
(0, 0) 1
(0, 1) 1
(0, 11) 1
(0, 9) 1
(0, 2) 1
(0, 6) 1
(0, 7) 1
(0, 5) 1
(0, 10) 1
(0, 3) 1
文本特征單詞列表:
['cosine' 'similarity' '兩個' '他們' '余弦' '值來' '向量' '夾角' '相似' '計算' '評估' '通過']
文本詞頻向量:
[[1 1 1 1 2 1 1 1 2 1 1 1]]
第三步:計算余弦相似度
讀取目標文本文件,對文件執(zhí)行相似度計算。
注:多個測試素材時,遍歷測試庫中的文本文件,對每個文件執(zhí)行相似度計算??梢娤挛膶嶒灤a。
第四步:獲取相似文本
根據(jù)需求設定一個閾值,將相似度大于閾值的文件視為相似文件,并按相似度結果排序,得到相似度最高的文本文件。可見下文實驗代碼。
?
2.2.4 測試
實驗素材:
場景1:比較2個文件相似性
實驗場景: 使用余弦相似度比較2個文件相似性,并可視化詞頻相似向量
實驗代碼:
"""
以圖搜圖:余弦相似度(Cosine Similarity)查找相似文本的原理與實現(xiàn)
實驗目的:比較2個文件相似性
實驗環(huán)境:Win10 | python 3.9.13 | OpenCV 4.4.0 | numpy 1.21.1 | Matplotlib 3.7.1 | jieba 0.42.1
實驗時間:2023-11-30
實例名稱:txtConsineSimilarity_v1.3.py
"""
import re
import numpy as np
import jieba
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
from sklearn.feature_extraction.text import CountVectorizer
# 預處理目標文本
def preprocess_text(text):
print(f"文本文件內容:{text}")
# 將文本轉換為小寫
text = text.lower()
print(f"將文本轉為小寫:{text}")
# 移除標點符號、數(shù)字和中文標點符號
text = re.sub(r'[^a-z\u4e00-\u9fa5\s]', '', text)
print(f"移除標點符號后:{text}")
# 使用 jieba 進行中文分詞
text_words = jieba.cut(text)
# 將分詞結果拼接成字符串
processed_text = ' '.join(text_words)
print(f"將分詞結果拼接成字符串:{processed_text}")
return processed_text
def cosine_similarity(vector1, vector2):
# 將二維列向量轉換為一維數(shù)組
vector1 = vector1.flatten()
vector2 = vector2.flatten()
# 算向量 vector1 和 vector2 的點積,即對應元素相乘后相加得到的標量值
dot_product = np.dot(vector1, vector2)
# 計算向量 vector1 的 L2 范數(shù),即向量各元素平方和的平方根
norm_vector1 = np.linalg.norm(vector1)
# 計算向量 vector2 的 L2 范數(shù)
norm_vector2 = np.linalg.norm(vector2)
# 避免除零錯誤
if norm_vector1 == 0 or norm_vector2 == 0:
return 0
# 利用余弦相似度公式計算相似度,即兩個向量的點積除以它們的 L2 范數(shù)之積
similarity = dot_product / (norm_vector1 * norm_vector2)
return similarity
# 獲取文件余弦相似度
def get_cosine_similarity(origin_file, target_file):
# 讀取原始文本
with open(origin_file, 'r', encoding='utf-8') as file:
origin_text = file.read()
# 預處理原始文本
origin_context = preprocess_text(origin_text)
print(f"預處理原始文本:{origin_context}")
# 構建文本向量:使用詞袋模型表示文本,過濾停用詞
origin_vectorizer = CountVectorizer(stop_words='english')
# 使用 CountVectorizer 將原始文本 origin_context 轉換為詞袋模型的向量表示
origin_vector = origin_vectorizer.fit_transform([origin_context])
print(f"原文件詞頻矩陣:\n{origin_vector}")
# 轉置矩陣,確保維度相同
origin_vector = origin_vector.T
# 獲取特征單詞列表
feature_names = origin_vectorizer.get_feature_names_out()
print(f"原文件特征單詞列表:{feature_names}")
print(f"原文件詞頻向量:\n{origin_vector.toarray()}")
with open(target_file, 'r', encoding='utf-8') as file:
target_text = file.read()
target_context = preprocess_text(target_text)
print(f"預處理目標文本:{target_context}")
# 構建文本向量:使用詞袋模型表示文本,過濾停用詞,并確保與查找源的向量維度一致
target_vectorizer = CountVectorizer(stop_words='english', vocabulary=feature_names)
target_vector = target_vectorizer.fit_transform([target_context])
print(f"目標文件詞頻矩陣:\n{target_vector}")
# 轉置矩陣,確保維度相同
target_vector = target_vector.T
print(f"目標文件轉置矩陣:\n{target_vector}")
print(f"目標文件詞頻向量:\n{target_vector.toarray()}")
# 計算余弦相似度
similarity = cosine_similarity(origin_vector.toarray(), target_vector.toarray())
print(f"文件 {target_file},與原文件 {origin_file} 的相似值:{similarity}")
# 可視化文本向量
show_text_vectors(origin_vector.toarray(), target_vector.toarray(), feature_names)
def show_text_vectors(origin_vector, target_vector, feature_names):
# 設置中文字體
font = FontProperties(fname="../../P0_Doc/fonts/msyh.ttc", size=12)
plt.figure(figsize=(10, 5))
plt.plot(feature_names, origin_vector, label='Original Text Vector')
plt.plot(feature_names, target_vector, label='Target Text Vector')
plt.title('Text Vector Comparison', fontproperties=font)
plt.xlabel('Feature Names', fontproperties=font)
plt.ylabel('Vector Values', fontproperties=font)
plt.xticks(rotation=90, fontproperties=font)
plt.legend()
plt.tight_layout()
plt.show()
if __name__ == "__main__":
# 本地測試文本素材庫
test_dir_path = '../../P0_Doc/txt_data/'
# 本地測試文本素材路徑
origin_file = test_dir_path + 'CosineSimilarity_定義_org.txt'
target_file = test_dir_path + 'CosineSimilarity_定義_v1.0.txt'
# 獲取文件余弦相似度
get_cosine_similarity(origin_file, target_file)
輸出打印:
文件 ../../P0_Doc/txt_data/CosineSimilarity_定義_v1.0.txt,與原文件 ../../P0_Doc/txt_data/CosineSimilarity_定義_org.txt 的相似值:0.9449111825230682
場景2:素材庫中查找文件相似性
實驗場景: 使用余弦相似度在目標素材庫中查找相似文件
實驗代碼:
"""
以圖搜圖:余弦相似度(Cosine Similarity)查找相似文本的原理與實現(xiàn)
實驗目的:使用余弦相似度在目標素材庫中查找相似文件
實驗環(huán)境:Win10 | python 3.9.13 | OpenCV 4.4.0 | numpy 1.21.1 | Matplotlib 3.7.1 | jieba 0.42.1
實驗時間:2023-11-30
實例名稱:txtConsineSimilarity_v1.4.py
"""
import os
import re
import time
import numpy as np
import jieba
from sklearn.feature_extraction.text import CountVectorizer
# 預處理目標文本
def preprocess_text(text):
# 將文本轉換為小寫
text = text.lower()
# 移除標點符號、數(shù)字和中文標點符號
text = re.sub(r'[^a-z\u4e00-\u9fa5\s]', '', text)
# 使用 jieba 進行中文分詞
text_words = jieba.cut(text)
# 將分詞結果拼接成字符串
processed_text = ' '.join(text_words)
return processed_text
def cosine_similarity(vector1, vector2):
# 將二維列向量轉換為一維數(shù)組
vector1 = vector1.flatten()
vector2 = vector2.flatten()
# 算向量 vector1 和 vector2 的點積,即對應元素相乘后相加得到的標量值
dot_product = np.dot(vector1, vector2)
# 計算向量 vector1 的 L2 范數(shù),即向量各元素平方和的平方根
norm_vector1 = np.linalg.norm(vector1)
# 計算向量 vector2 的 L2 范數(shù)
norm_vector2 = np.linalg.norm(vector2)
# 避免除零錯誤
if norm_vector1 == 0 or norm_vector2 == 0:
return 0
# 利用余弦相似度公式計算相似度,即兩個向量的點積除以它們的 L2 范數(shù)之積
similarity = dot_product / (norm_vector1 * norm_vector2)
return similarity
# 獲取文件余弦相似度
def get_cosine_similarity(origin_file, test_files):
# 讀取原始文本
with open(origin_file, 'r', encoding='utf-8') as file:
origin_text = file.read()
# 預處理原始文本
origin_context = preprocess_text(origin_text)
# 構建文本向量:使用詞袋模型表示文本,過濾停用詞
origin_vectorizer = CountVectorizer(stop_words='english')
# 使用 CountVectorizer 將原始文本 origin_context 轉換為詞袋模型的向量表示
origin_vector = origin_vectorizer.fit_transform([origin_context])
# 轉置矩陣,確保維度相同
origin_vector = origin_vector.T
# 獲取特征單詞列表
feature_names = origin_vectorizer.get_feature_names_out()
# 遍歷測試庫中的文本文件,獲取文件余弦相似度
for filename in test_files:
with open(filename, 'r', encoding='utf-8') as file:
target_text = file.read()
target_context = preprocess_text(target_text)
# 構建文本向量:使用詞袋模型表示文本,過濾停用詞,并確保與查找源的向量維度一致
target_vectorizer = CountVectorizer(stop_words='english', vocabulary=feature_names)
target_vector = target_vectorizer.fit_transform([target_context])
# 轉置矩陣,確保維度相同
target_vector = target_vector.T
# 計算余弦相似度
similarity = cosine_similarity(origin_vector.toarray(), target_vector.toarray())
print(f"文件 {os.path.basename(filename)},與原文件 {os.path.basename(origin_file)} 的相似值:{similarity}")
# 根據(jù)需求設定一個閾值,將相似度大于閾值的文件視為相似文件,并按相似度結果排序,得到相似度最高的文本文件
if (similarity >= 0.9):
text_similarities.append((filename, similarity))
if __name__ == "__main__":
time_start = time.time()
# 本地測試文本素材庫
test_dir_path = '../../P0_Doc/txt_data/'
# 本地測試文本素材路徑
origin_file = test_dir_path + 'CosineSimilarity_org.txt'
# 指定測試文本文件擴展名
txt_suffix = ['.txt', '.doc', '.md']
# 獲取素材庫文件夾中所有文件路徑
all_files = [os.path.join(test_dir_path, filename) for filename in os.listdir(test_dir_path)]
# 篩選出指定后綴的文件
test_files = [file for file in all_files if any(file.endswith(suffix) for suffix in txt_suffix)]
# 獲取素材庫文件夾中文件余弦相似度
text_similarities = []
get_cosine_similarity(origin_file, test_files)
# 按相似度降序排序
text_similarities.sort(key=lambda item: item[1], reverse=True)
print(f"按相似度降序排序:{text_similarities}")
# 打印相似度最高的文本文件
print(f"相似度最高的文本文件: {text_similarities[0][0]}, 相似度: {float(text_similarities[0][1]):.4f}")
time_end = time.time()
print(f"耗時:{time_end - time_start}")
輸出打?。?/p>
文件 CosineSimilarity_org.txt,與原文件 CosineSimilarity_org.txt 的相似值:1.0
文件 CosineSimilarity_v1.0_拷貝版.doc,與原文件 CosineSimilarity_org.txt 的相似值:1.0
文件 CosineSimilarity_v1.1_位置調換版.md,與原文件 CosineSimilarity_org.txt 的相似值:1.0
文件 CosineSimilarity_v1.2_純代碼版.txt,與原文件 CosineSimilarity_org.txt 的相似值:0.6402964041311439
文件 CosineSimilarity_v1.3_刪減版.txt,與原文件 CosineSimilarity_org.txt 的相似值:0.9704511815935536
文件 CosineSimilarity_v1.4_刪減版2.txt,與原文件 CosineSimilarity_org.txt 的相似值:0.4919253465224834
文件 CosineSimilarity_v1.5_無可視化版.txt,與原文件 CosineSimilarity_org.txt 的相似值:0.9811481821202109
文件 CosineSimilarity_v1.6_復雜版.txt,與原文件 CosineSimilarity_org.txt 的相似值:0.8590656537770545
文件 CosineSimilarity_定義_org.txt,與原文件 CosineSimilarity_org.txt 的相似值:0.3587392083132991
文件 CosineSimilarity_定義_v1.0.txt,與原文件 CosineSimilarity_org.txt 的相似值:0.3311241245802555
按相似度降序排序:[('../../P0_Doc/txt_data/CosineSimilarity_org.txt', 1.0), ('../../P0_Doc/txt_data/CosineSimilarity_v1.0_拷貝版.doc', 1.0), ('../../P0_Doc/txt_data/CosineSimilarity_v1.1_位置調換版.md', 1.0), ('../../P0_Doc/txt_data/CosineSimilarity_v1.5_無可視化版.txt', 0.9811481821202109), ('../../P0_Doc/txt_data/CosineSimilarity_v1.3_刪減版.txt', 0.9704511815935536)]
相似度最高的文本文件: ../../P0_Doc/txt_data/CosineSimilarity_org.txt, 相似度: 1.0000
耗時:0.6692209243774414
?
2.2.5 實驗總結
余弦相似度通常在處理大規(guī)模文本數(shù)據(jù)時具有較好的性能,但對于一些需要考慮語法和語義信息的任務,可能需要使用更復雜的模型或度量方法。
優(yōu)點
- 簡單有效:余弦相似度的計算方法相對簡單,容易理解和實現(xiàn)。這使得它成為許多文本相似性比較任務的首選方法之一。
- 不受文本長度影響: 余弦相似度不受文本長度的影響,只受文本向量的方向角度影響。因此,對于不同長度的文本,余弦相似度可以更公正地評估它們之間的相似性。
- 適用于高維空間: 在高維空間中,余弦相似度的性能通常比歐幾里德距離等其他相似性度量更好。這使其在自然語言處理中處理文本向量時非常有用。
缺點
- 不考慮詞序信息: 余弦相似度只考慮文本中詞匯的出現(xiàn)頻率,而不考慮它們的順序。這意味著它可能無法捕捉到語法結構或上下文信息,對于語義上相似但詞序不同的文本可能判斷不準確。
- 對稀疏向量不敏感: 當文本表示為稀疏向量時(比如使用詞袋模型),余弦相似度可能對于共享少量相同詞匯的文本給出相似性度量過高的結果,因為它只關注共同出現(xiàn)的詞,而不考慮它們的重要性。
- 無法處理一詞多義: 余弦相似度在處理一詞多義時存在問題,因為它只基于詞匯的出現(xiàn)頻率而不考慮語境。同一個詞在不同的上下文中可能有不同的含義,但余弦相似度無法捕捉這種語義信息。
?
2.2.6 實驗異常
異?,F(xiàn)象1
Traceback (most recent call last):
File "d:\Ct_ iSpace\Wei\Python\iPython\T30_Algorithm\P2_Algo\02_CosineSimilarity\imgCosine_v2.1.py", line 56, in <module>
text_similarities = get_cosine_similarity(test_dir_path)
File "d:\Ct_ iSpace\Wei\Python\iPython\T30_Algorithm\P2_Algo\02_CosineSimilarity\imgCosine_v2.1.py", line 34, in get_cosine_similarity
similarity = cosine_similarity(origin_vector.toarray(), target_vector.toarray())
File "D:\Tp_Mylocal\20_Install\python-3.9.13\lib\site-packages\sklearn\metrics\pairwise.py", line 1393, in cosine_similarity
X, Y = check_pairwise_arrays(X, Y)
File "D:\Tp_Mylocal\20_Install\python-3.9.13\lib\site-packages\sklearn\metrics\pairwise.py", line 180, in check_pairwise_arrays
raise ValueError(
ValueError: Incompatible dimension for X and Y matrices: X.shape[1] == 107 while Y.shape[1] == 100
異常原因: 2個向量維度不一致。
問題出現(xiàn)在 cosine_similarity 函數(shù)的調用上。cosine_similarity 函數(shù)的參數(shù) X 和 Y 應該是形狀相同的矩陣,但是在測試代碼中,origin_vector 和 target_vector 的維度不一致。即,2個測試文件的行數(shù)不對等。
在這里,origin_vector 是由原始文本構建的文本向量,而 target_vector 是由目標文本構建的文本向量。這兩個向量的維度應該是相同的,以便進行余弦相似度的計算。
注: from sklearn.metrics.pairwise import cosine_similarity,scikit-learn 的 cosine_similarity 函數(shù)的輸入是兩個形狀相同的矩陣。
?
異常現(xiàn)象2
文件名稱 CosineSimilarity_v2.1.txt,與目標文件 ../../P0_Doc/txt_data/CosineSimilarity_org.txt 的相似值:[[1. 1. 1. ... 1. 1. 1.]
[1. 1. 1. ... 1. 1. 1.]
[1. 1. 1. ... 1. 1. 1.]
...
[1. 1. 1. ... 1. 1. 1.]
[1. 1. 1. ... 1. 1. 1.]
[1. 1. 1. ... 1. 1. 1.]]
Traceback (most recent call last):
File "d:\Ct_ iSpace\Wei\Python\iPython\T30_Algorithm\P2_Algo\02_CosineSimilarity\imgCosine_v2.1.py", line 74, in <module>
text_similarities.sort(key=lambda item: item[1], reverse=True)
ValueError: operands could not be broadcast together with shapes (101,34) (101,31)
異常原因: 2個向量維度不一致。
注:錯誤的信息顯示兩個數(shù)組的形狀分別是 (107, 37) 和 (107, 34),這說明兩個數(shù)組的列數(shù)不同,元素的形狀 (shape) 不匹配,導致無法進行排序。
這個問題可能是由于某些文本文件的長度(詞的數(shù)量)與其他文件不同,導致余弦相似度計算時形狀不一致。你可以在計算余弦相似度之前,將向量長度調整為一致的。
?
異常現(xiàn)象3
similarity = cosine_similarity(origin_vector.toarray(), target_vector.toarray())
File "d:\Ct_ iSpace\Wei\Python\iPython\T30_Algorithm\P2_Algo\02_CosineSimilarity\imgCosine_v2.1.py", line 23, in cosine_similarity
dot_product = np.dot(vector1, vector2)
File "<__array_function__ internals>", line 5, in dot
ValueError: shapes (101,1) and (101,1) not aligned: 1 (dim 1) != 101 (dim 0)
異常原因: 2個向量維度不一致。
解決方案:可參考上述實驗二完整代碼。
注:可以將 origin_vector 和 target_vector 轉置后再計算余弦相似度。目的是確保目標文本向量與原始文本向量具有相同的維度。
在使用 OpenCV 進行余弦相似度計算時,可能會遇到目標文本向量與原始文本向量維度不一致的問題。這可能是因為在構建文本向量時,使用的文本處理方法或者參數(shù)不同導致的??梢酝ㄟ^以下方法嘗試解決維度不一致的問題:
- 檢查文本內容是否正確:確保你讀取的文本文件中的內容沒有問題。你可以打印出原始文本和目標文本,檢查是否包含了無效字符或者其他異常。
- 檢查文本向量的維度:在構建文本向量后,使用 .shape 屬性檢查它們的維度。確保它們的維度是相同的。
?
異常現(xiàn)象4
Traceback (most recent call last):
File "d:\Ct_ iSpace\Wei\Python\iPython\T30_Algorithm\P2_Algo\02_CosineSimilarity\txtCosine_v2.1 copy.py", line 102, in <module>
get_cosine_similarity(origin_file, target_file)
File "d:\Ct_ iSpace\Wei\Python\iPython\T30_Algorithm\P2_Algo\02_CosineSimilarity\txtCosine_v2.1 copy.py", line 50, in get_cosine_similarity
origin_vector = origin_vectorizer.fit_transform([origin_context])
File "D:\Tp_Mylocal\20_Install\python-3.9.13\lib\site-packages\sklearn\feature_extraction\text.py", line 1388, in fit_transform
vocabulary, X = self._count_vocab(raw_documents, self.fixed_vocabulary_)
File "D:\Tp_Mylocal\20_Install\python-3.9.13\lib\site-packages\sklearn\feature_extraction\text.py", line 1294, in _count_vocab
raise ValueError(
ValueError: empty vocabulary; perhaps the documents only contain stop words
異常原因: 這個錯誤表明在文本預處理過程中,由于某些原因導致詞匯表為空。這通常發(fā)生在文本中只包含停用詞或特定的無效文本內容,導致無法構建有效的詞匯表。
?
3. 環(huán)境依賴
如果 Matplotlib 庫沒有安裝,可以使用以下命令安裝:
pip install matplotlib
查看 Matplotlib 版本號:
import matplotlib
print("matplotlib 版本號:", matplotlib.__version__)
如果 jieba 庫沒有安裝,可以使用以下命令安裝:
pip install jieba
查看 jieba 版本號:
import jieba
print("jieba 版本號:", jieba.__version__)
?文章來源:http://www.zghlxwxcb.cn/news/detail-820611.html
4. 系列書簽
均值哈希算法: OpenCV書簽 #均值哈希算法的原理與相似圖片搜索實驗
感知哈希算法: OpenCV書簽 #感知哈希算法的原理與相似圖片搜索實驗
差值哈希算法: OpenCV書簽 #差值哈希算法的原理與相似圖片搜索實驗
直方圖算法: OpenCV書簽 #直方圖算法的原理與相似圖片搜索實驗
余弦相似度: OpenCV書簽 #余弦相似度的原理與相似圖片/相似文件搜索實驗文章來源地址http://www.zghlxwxcb.cn/news/detail-820611.html
到了這里,關于OpenCV書簽 #余弦相似度的原理與相似圖片/相似文件搜索實驗的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!