python勾股定理中三个数的关系是、找出三十以内的_从勾股定理到余弦相似度-程序员的数学基础...
大部分程序員由于理工科的背景,有一些高數、線性代數、概率論與數理統計的數學基礎。所以當機器學習的熱潮來臨的時候,都躍躍欲試,對機器學習的算法以及背后的數學思想有比較強烈的探索欲望。
本文的作者就是其中的一位。然而實踐的過程中,又發現數學知識的理解深度有些欠缺,在理解一些公式背后的意義時,有些力不從心的感覺。因此梳理了一些數學上的知識盲點,理順自己的知識脈絡,順便分享給有需要的人。
本文主要講解余弦相似度的相關知識點。相似度計算用途相當廣泛,是搜索引擎、推薦引擎、分類聚類等業務場景的核心點。為了理解清楚余弦相似度的來龍去脈,我將會從最簡單的初中數學入手,逐步推導出余弦公式。然后基于余弦公式串講一些實踐的例子。
一、業務背景
通常我們日常開發中,可能會遇到如下的業務場景。
精準營銷,圖像處理,搜索引擎 這三個看似風馬牛不相及的業務場景,其實面臨一個共同的問題就是相似度的計算。例如精準營銷中的人群擴量涉及用戶相似度的計算;圖像分類問題涉及圖像相似度的計算,搜索引擎涉及查詢詞和文檔的相似度計算。相似度計算中,可能由于《數學之美》的影響,大家最熟悉的應該是余弦相似度。那么余弦相似度是怎么推導出來的呢?
二、數學基礎
理解余弦相似度,要從理解金字塔開始。我們知道金字塔的底座是一個巨大的正方形。例如吉薩大金字塔的邊長超過230m。構造這樣一個巨大的正方形,如何保證構造出來的圖形不走樣呢?比如如何確保構造的結果不是菱形或者梯形。
1、勾股定理
要保證構造出來的四邊形是正方形,需要保證兩個點:其一是四邊形的邊長相等;其二是四邊形的角是直角。四邊形的邊長相等很容易解決,在工程實踐中,取一根定長的繩子作為邊長就可以了。如何保障直角呢?古人是利用勾股定理解決的,更切確地說是勾股定理的逆定理。
構造一個三角形,保證三角形的三邊長分別是3,4,5。那么邊長為5的邊對應的角為直角。中國有個成語“無規矩不成方圓”,其中的矩,就是直角的尺。
勾股證明是初中數學的知識,理解很容易。證明也很簡單,據說愛因斯坦11歲就發現了一種證明方法。勾股定理的證明方法據統計有超過400種, 感興趣的同學可以自行了解。另勾股定理也是費馬大定理的靈感來源,費馬大定理困擾了世間智者300多年,也誕生了很多的逸聞趣事,這里不贅述。
2、余弦定理
勾股定理存在著一個很大的限制,就是要求三角形必須是直角三角形。那么對于普通的三角形,三個邊存在什么樣的關系呢?這就引出了余弦定理。
余弦定理指出了任意三角形三邊的關系,也是初中就可以理解的數學知識,證明也比較簡單,這里就略過了。
其實對于三角形,理解了勾股定理和余弦定理。就已經掌握了三角形的很多特性和秘密了。比如根據等邊三角形,可以推導出cos(60)=1/2。但是如果想理解幾何更多的秘密,就需要進入解析幾何的世界。這個數學知識也不算很高深,高中數學知識。
這里我們理解最簡單就可以了,那就是三角形在直角坐標系中的表示。所謂“橫看成嶺側成峰,遠近高低各不同”,我們可以理解為三角形的另一種表現形式。
比如我們可以用a,b,c三個邊描述一個三角形;在平面直角坐標系中,我們可以用兩個向量表示一個三角形。
3、余弦相似度
當我們引入了直角坐標系后,三角形的表示就進入了更靈活、更強大和更抽象的境界了。幾何圖形可以用代數的方法來計算,代數可以用幾何圖形形象化表示,大大降低理解難度。比如我們用一個向量來表示三角形的一個邊,就可以從二維空間直接擴展到高維空間。
這里,向量的定義跟點是一樣的;向量的乘法也只是各個維度值相乘累加;向量的長度看似是新的東西,其實繞了一個圈,本質上還是勾股定理,只是勾股定理從二維空間擴展到了N維空間而已。而且向量長度又是兩個相同向量乘法的特例。數學的嚴謹性在這里體現得淋漓盡致。
結合勾股定理,余弦定理,直角坐標系,向量。我們就可以很自然地推導出余弦公式了,這里唯一的理解難點就是勾股定理和余弦定理都是用向量來表示。
得到了余弦公式后,我們該怎么理解余弦公式呢?
極端情況下,兩個向量重合了,就代表兩個向量完全相似。然而這里的完全相似,其實是指向量的方向。向量有方向和長度兩個要素,這里只使用方向這一個要素,在實踐中就埋下了隱患。但是畢竟一個數學模型建立起來了。我們可以用這個模型解決一些實際中的問題了。
所謂數學模型,有可能并不需要高深的數學知識,對外的表現也僅僅是一個數學公式。比如余弦定理這個數學模型,高中數學知識就足夠理解了。而且關于模型,有這樣一個很有意思的論述:“所有的數學模型都是錯的,但是有些是有用的”。這里我們更多關注其有用的一面。
理解了向量夾角,那么該怎么理解向量呢?它僅僅是三角形的一條邊嗎?
人生有幾何,萬物皆向量。向量在數學上是簡單的抽象,這個抽象我們可以用太多實際的場景來使它落地。比如用向量來指代用戶標簽,用向量來指代顏色,用向量來指代搜索引擎的邏輯...
三、業務實踐
理解了余弦定理,理解了數學建模的方式。接下來我們就可以做一些有意思的事情了。比如前面提到的三個業務場景,我們可以看看如何用余弦相似度來解決。當然實際問題肯定遠遠要復雜得多,但是核心的思想都是類似的。
案例1:精準營銷
假設一次運營計劃,比如我們圈定了1w的用戶,如何擴展到10萬人呢?
利用余弦相似度,我們這里其實最核心的問題就是:如何將用戶向量化?
將每個用戶視為一個向量,用戶的每個標簽值視為向量的一個維度。當然這里實際工程中還有特征歸一化,特征加權等細節。我們這里僅作為演示,不陷入到細節中。
對于人群,我們可以取人群中,所有用戶維度值的平均值,作為人群向量。這樣處理后,就可以使用余弦公式計算用戶的相似度了。
我們通過計算大盤用戶中每個用戶跟圈定人群的相似度,取topN即可實現人群的擴量。
直接“show me the code”吧!
# -*- coding: utf-8 -*-
import numpy as np
import numpy.linalg as linalg
def cos_similarity(v1, v2):
num = float(np.dot(v1.T, v2)) # 若為行向量則 A.T * B
denom = linalg.norm(v1) * linalg.norm(v2)
if denom > 0:
cos = num / denom # 余弦值
sim = 0.5 + 0.5 * cos # 歸一化
return sim
return 0
if __name__ == ‘__main__‘:
u_tag_list = [
["女", "26", "是", "白領"],
["女", "35", "是", "白領"],
["女", "30", "是", "白領"],
["女", "22", "是", "白領"],
["女", "20", "是", "白領"]
]
new_user = ["女", "20", "是", "白領"]
u_tag_vector = np.array([
[1, 26, 1, 1],
[1, 35, 1, 1],
[1, 30, 1, 1],
[1, 22, 1, 1],
[1, 20, 1, 1]
])
c1 = u_tag_vector[0]
c1 += u_tag_vector[1]
c1 += u_tag_vector[2]
c1 += u_tag_vector[3]
c1 += u_tag_vector[4]
c1 = c1/5
new_user_v1 = np.array([1, 36, 1, 1])
new_user_v2 = np.array([-1, 20, 0, 1])
print("vector-u1: ", list(map(lambda x: ‘%.2f‘ % x, new_user_v1.tolist()[0:10])))
print("vector-u2: ", list(map(lambda x: ‘%.2f‘ % x, new_user_v2.tolist()[0:10])))
print("vector-c1: ", list(map(lambda x: ‘%.2f‘ % x, c1.tolist()[0:10])))
print("sim: ", cos_similarity(c1, new_user_v1))
print("sim: ", cos_similarity(c1, new_user_v2))
案例2:圖像分類
有兩類圖片,美食和萌寵。對于新的圖片,如何自動分類呢?
這里我們的核心問題是:圖片如何向量化?
圖片由像素構成,每個像素有RGB三個通道。由于像素粒度太細,將圖片分割成大小相對的格子,每個格子定義3個維度,維度值取格子內像素均值。
下面也是給出樣例代碼:
# -*- coding: utf-8 -*-
import numpy as np
import numpy.linalg as linalg
import cv2
def cos_similarity(v1, v2):
num = float(np.dot(v1.T, v2)) # 若為行向量則 A.T * B
denom = linalg.norm(v1) * linalg.norm(v2)
if denom > 0:
cos = num / denom # 余弦值
sim = 0.5 + 0.5 * cos # 歸一化
return sim
return 0
def build_image_vector(im):
"""
:param im:
:return:
"""
im_vector = []
im2 = cv2.resize(im, (500, 300))
w = im2.shape[1]
h = im2.shape[0]
h_step = 30
w_step = 50
for i in range(0, w, w_step):
for j in range(0, h, h_step):
each = im2[j:j+h_step, i:i+w_step]
b, g, r = each[:, :, 0], each[:, :, 1], each[:, :, 2]
im_vector.append(np.mean(b))
im_vector.append(np.mean(g))
im_vector.append(np.mean(r))
return np.array(im_vector)
def show(imm):
imm2 = cv2.resize(imm, (510, 300))
print(imm2.shape)
imm3 = imm2[0:50, 0:30]
cv2.imshow("aa", imm3)
cv2.waitKey()
cv2.destroyAllWindows()
imm4 = imm2[51:100, 0:30]
cv2.imshow("bb", imm4)
cv2.waitKey()
cv2.destroyAllWindows()
imm2.fill(0)
def build_image_collection_vector(p_name):
path = "D:\python-workspace\cos-similarity\images\"
c1_vector = np.zeros(300)
for pic in p_name:
imm = cv2.imread(path + pic)
each_v = build_image_vector(imm)
a=list(map(lambda x:‘%.2f‘ % x, each_v.tolist()[0:10]))
print("p1: ", a)
c1_vector += each_v
return c1_vector/len(p_name)
if __name__ == ‘__main__‘:
v1 = build_image_collection_vector(["food1.jpg", "food2.jpg", "food3.jpg"])
v2 = build_image_collection_vector(["pet1.jpg", "pet2.jpg", "pet3.jpg"])
im = cv2.imread("D:\python-workspace\cos-similarity\images\pet4.jpg")
v3 = build_image_vector(im)
print("v1,v3:", cos_similarity(v1,v3))
print("v2,v3:", cos_similarity(v2,v3))
a = list(map(lambda x: ‘%.2f‘ % x, v3.tolist()[0:10]))
print("p1: ", a)
im2 = cv2.imread("D:\python-workspace\cos-similarity\images\food4.jpg")
v4 = build_image_vector(im2)
print("v1,v4:", cos_similarity(v1, v4))
print("v2,v4:", cos_similarity(v2, v4))
至于代碼中用到的圖片,用戶可以自行收集即可。筆者也是直接從搜索引擎中截取的。程序計算的結果也是很直觀的,V2(萌寵)跟圖像D1的相似度為0.956626,比V1(美食)跟圖像D1的相似度0.942010更高,所以結果也是很明確的。
案例3:文本檢索
假設有三個文檔,描述的內容如下。一個是疫情背景下,蘋果公司的資訊,另外兩個是水果相關的信息。輸入搜索詞“蘋果是我最喜歡的水果”,? 該怎么找到最相關的文檔?
這里的核心問題也是文本和搜索詞如何向量化?
這里其實可以把搜索詞也視為文檔,這樣問題就簡化成:文檔如何向量化?
出于簡化問題的角度,我們可以給出最簡單的答案:文檔由詞組成,每個詞作為一個維度;文檔中詞出現的頻率作為維度值。
當然,實際操作時我們維度值的計算會更復雜一些,比如用TF-IDF。這里用詞頻(TF)并不影響演示效果,所以我們從簡。
將文本向量化后,剩下也是依樣畫葫蘆,用余弦公式計算相似度, 流程如下:
最后,給出代碼:
# -*- coding: utf-8 -*-
import numpy as np
import numpy.linalg as linalg
import jieba
def cos_similarity(v1, v2):
num = float(np.dot(v1.T, v2)) # 若為行向量則 A.T * B
denom = linalg.norm(v1) * linalg.norm(v2)
if denom > 0:
cos = num / denom # 余弦值
sim = 0.5 + 0.5 * cos # 歸一化
return sim
return 0
def build_doc_tf_vector(doc_list):
num = 0
doc_seg_list = []
word_dic = {}
for d in doc_list:
seg_list = jieba.cut(d, cut_all=False)
seg_filterd = filter(lambda x: len(x)>1, seg_list)
w_list = []
for w in seg_filterd:
w_list.append(w)
if w not in word_dic:
word_dic[w] = num
num+=1
doc_seg_list.append(w_list)
print(word_dic)
doc_vec = []
for d in doc_seg_list:
vi = [0] * len(word_dic)
for w in d:
vi[word_dic[w]] += 1
doc_vec.append(np.array(vi))
print(vi[0:40])
return doc_vec, word_dic
def build_query_tf_vector(query, word_dic):
seg_list = jieba.cut(query, cut_all=False)
vi = [0] * len(word_dic)
for w in seg_list:
if w in word_dic:
vi[word_dic[w]] += 1
return vi
if __name__ == ‘__main__‘:
doc_list = [
"""
受全球疫情影響,3月蘋果宣布關閉除大中華區之外數百家全球門店,其龐大的供應鏈體系也受到沖擊,
盡管目前富士康等代工廠已經開足馬力恢復生產,但相比之前產能依然受限。中國是iPhone生產的大本營,
為了轉移風險,iPhone零部件能否實現印度制造?實現印度生產的最大難點就是,相對中國,印度制造業仍然欠發達
""",
"""
蘋果是一種低熱量的水果,每100克產生大約60千卡左右的熱量。蘋果中營養成分可溶性大,容易被人體吸收,故有“活水”之稱。
它有利于溶解硫元素,使皮膚潤滑柔嫩。
""",
"""
在生活當中,香蕉是一種很常見的水果,一年四季都能吃得著,因其肉質香甜軟糯,且營養價值高,所以深受老百姓的喜愛。
那么香蕉有什么具體的功效,你了解嗎?
"""
]
query = "蘋果是我喜歡的水果"
doc_vector, word_dic = build_doc_tf_vector(doc_list)
query_vector = build_query_tf_vector(query, word_dic)
print(query_vector[0:35])
for i, doc in enumerate(doc_vector):
si = cos_similarity(doc, query_vector)
print("doc", i, ":", si)
我們檢索排序的結果如下:
文檔D2是相似度最高的,符合我們的預期。這里我們用最簡單的方法,實現了一個搜索打分排序的樣例,雖然它并沒有實用價值,但是演示出了搜索引擎的工作原理。
四、超越余弦
前面通過簡單的3個案例,演示了余弦定理的用法,但是沒有完全釋放出余弦定理的洪荒之力。接下來展示一下工業級的系統中是如何使用余弦定理的。這里選取了開源搜索引擎數據庫ES的內核Lucene作為研究對象。研究的問題是:Lucene是如何使用余弦相似度進行文檔相似度打分?
當然,對于Lucene的實現,它有另一個名字:向量空間模型。即許多向量化的文檔集合形成了向量空間。我們首先直接看公式:
很明顯,實際公式跟理論公式長相差異很大。那么我們怎么理解呢?換言之,我們怎么基于理論公式推導出實際公式呢?
首先需要注意的是,在Lucene中,文檔向量的特征不再是我們案例3中展示的,用的詞頻,而是TF-IDF。關于TF-IDF相關的知識,比較簡單,主要的思路在于:
如何量化一個詞在文檔中的關鍵程度?? TF-IDF給出的答案是綜合考慮詞頻(詞在當前文檔中出現的次數)以及逆文檔頻率(詞出現的文檔個數)兩個因素。
詞在當前文檔中出現次數(TF)越多,? 詞越重要
詞在其他文檔出現的次數(IDF)越少,詞越獨特
感興趣的話,可以自行參考其他資料,這里不展開說明。
回到我們的核心問題:?我們怎么基于理論公式推導出實際公式呢?
四步走就可以了,如下圖:
第一步:計算向量乘法
向量乘法就是套用數學公式了。這里需要注意的是,這里有兩個簡化的思想:
查詢語句中不存在的詞tf(t,q)=0
查詢語句基本沒有重復的詞tf(t,q)=1
所以我們比較簡單完成了第一步推導:
第二步: 計算查詢語句向量長度|V(q)|
計算向量長度,其實就是勾股定理的使用了。只不過這里是多維空間的勾股定理。
這里取名queryNorm, 表示這個操作是對向量的歸一化。這個其實是當向量乘以queryNorm后,就變成了單位向量。單位向量的長度為1,所以稱為歸一化,也就是取名norm。理解了這一層,看lucene源碼的時候,就比較容易理解了。這正如瑯琊榜的臺詞一樣:問題出自朝堂,答案卻在江湖。這里是問題出自Lucene源碼,答案卻在數學。
第三步:計算文檔向量長度|V(d)|
這里其實是不能沿用第二步的做法的。前面已經提到,向量有兩大要素:方向和長度。余弦公式只考慮了方向因素。這樣在實際應用中,余弦相似度就是向量長度無關的了。
這在搜索引擎中,如果查詢語句命中了長文檔和短文檔,按照余弦公式TF-IDF特征,偏向于對短小的文檔打較高的分數。對長文檔不公平,所以需要優化一下。
這里的優化思路就是采用文檔詞個數累積,從而降低長文檔和短文檔之間的差距。當然這里的業務訴求可能比較多樣,所以在源碼實現的時候,開放了接口允許用戶自定義。借以提升靈活度。
第四步:混合用戶權重和打分因子
所謂用戶權重,就是指用戶指定查詢詞的權重。例如典型地競價排名就是人為提升某些查詢詞的權重。所謂打分因子,即如果一個文檔中相比其它的文檔出現了更多的查詢關鍵詞,那么其值越大。綜合考慮了多詞查詢的場景。經過4步,我們再看推導出來的公式和實際公式,發現相似度非常高。
推導公式和官方公式基本就一致了。
五、總結
本文簡單介紹了余弦相似度的數學背景。從埃及金字塔的建設問題出發,引出了勾股定理,進而引出了余弦定理。并基于向量推導出來了余弦公式。
接下來通過三個業務場景的例子,介紹余弦公式的應用,即數學模型如何落地到業務場景中。這三個簡單的例子代碼不過百行,能夠幫助讀者更好地理解余弦相似度。
最后介紹了一個工業級的樣例。基于Lucene構建的ES是當前最火熱的搜索引擎解決方案。學習余弦公式在Lucene中落地,有助于理解業界的真實玩法。進一步提升對余弦公式的理解。
六、參考文獻
作者:Shuai Guangying
總結
以上是生活随笔為你收集整理的python勾股定理中三个数的关系是、找出三十以内的_从勾股定理到余弦相似度-程序员的数学基础...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux锐捷代码_告诉你Ubuntu
- 下一篇: 74hc165C语言程序,单片机驱动74