作者|Maria Malitckaya
編譯|VK
來源|Towards Data Science
改進機器學習模型的一個有效方法是使用詞嵌入,使用詞嵌入,你可以捕獲檔案中單詞的背景關系,然后找到語意和語法上的相似之處,
在這篇文章中,我們將討論詞嵌入技術的一個不尋常的應用,我們將嘗試使用OpenAPI的規范作為資料集在其中找到最好的詞嵌入技術,作為OpenAPI規范的一個例子,我們將使用OpenAPI specifications(https://swagger.io/specification/)提供的OpenAPI規范資料集,
最大的挑戰是,OpenAPI規范既不是自然語言,也不是代碼,但這也意味著我們可以自由使用任何可用的嵌入模型,在這個實驗中,我們將研究三種可能可行的候選方案:code2vec、glow和spaCy,
code2vec是一個神經模型,可以學習與源代碼相關的類比,該模型是在Java代碼資料庫上訓練的,但是你可以將其應用于任何代碼,
還有GloVe,GloVe是一種常用的自然語言處理演算法,它是在維基百科和Gigawords上訓練的,
最后,我們有了spaCy,雖然spaCy是最近才發展起來的,但該演算法已經以世界上最快的詞嵌入而聞名,
讓我們看看這些演算法中哪一種更適合OpenAPI資料集,哪種演算法對于OpenAPI規范的運行速度更快. 我把這篇文章分為六個部分,每個部分都包含代碼示例和一些將來使用的提示,外加一個結論,
-
下載資料集
-
下載詞匯表
-
提取欄位名稱
-
標識化
-
創建欄位名稱的資料集
-
測驗嵌入
-
結論
現在,我們可以開始了,
1.下載資料集
首先,我們需要下載整個apis-guru資料庫:https://apis.guru/,
你會注意到,大多數apis-guru規范都是Swagger 2.0格式,但是,OpenAPI規范的最新版本是OpenAPI 3.0,
因此,讓我們使用Unmock腳本將整個資料集轉換為這種格式!你可以按照Unmock openapi腳本的README檔案中的說明完成此操作:https://github.com/meeshkan/unmock-openapi-scripts/blob/master/README.md,
這可能需要一段時間,最后,你將得到一個大資料集,
2.下載詞匯表
code2vec
-
從code2vec GitHub頁面下載模型,按照快速入門部分中的說明進行操作,
-
使用gensim庫加載,
model = word2vec.load_word2vec_format(vectors_text_path, binary=False)
GloVe
-
從網站下載一個GloVe詞匯表,我們選了最大的一個,因為這樣它就更有可能找到我們所有的單詞,你可以選擇下載它的位置,但為了方便起見,最好將其存盤在作業目錄中,
-
手動加載GloVe詞匯表,
embeddings_dict = {}
with open("../glove/glove.6B.300d.txt", 'r', encoding="utf-8") as f:
for line in f:
values = line.split()
word = values[0]
vector = np.asarray(values[1:], "float32")
embeddings_dict[word] = vector
spaCy
加載spaCy的詞匯表:
nlp = spacy.load(‘en_core_web_lg’).
3.提取欄位名
OpenAPI規范名稱的整個串列可以從scripts/fetch-list.sh檔案或使用以下函式(對于Windows)獲取:
def getListOfFiles(dirName):
listOfFile = os.listdir(dirName)
allFiles = list()
for entry in listOfFile:
fullPath = posixpath.join(dirName, entry)
if posixpath.isdir(fullPath):
allFiles = allFiles + getListOfFiles(fullPath)
else:
allFiles.append(fullPath)
return allFiles
另一個大問題是從我們的OpenAPI規范中獲取欄位名,為此,我們將使用openapi-typed庫,讓我們定義一個get_fields函式,該函式接受OpenAPI規范并回傳欄位名串列:
def get_fields_from_schema(o: Schema) -> Sequence[str]:
return [
*(o['properties'].keys() if ('properties' in o) and (type(o['properties']) == type({})) else []),
*(sum([
get_fields_from_schema(schema) for schema in o['properties'].values() if not ('$ref' in schema) and type(schema) == type({})], []) if ('properties' in o) and ($ *(get_fields_from_schema(o['additionalProperties']) if ('additionalProperties' in o) and (type(o['additionalProperties']) == type({})) else []),
*(get_fields_from_schema(o['items']) if ('items' in o) and (type(o['items'] == type({}))) else []),
]
def get_fields_from_schemas(o: Mapping[str, Union[Schema, Reference]]) -> Sequence[str]:
return sum([get_fields_from_schema(cast(Schema, maybe_schema)) for maybe_schema in o.values() if not ('$ref' in maybe_schema) and (type(maybe_schema) == type({}))], [])
def get_fields_from_components(o: Components) -> Sequence[str]:
return [
*(get_fields_from_schemas(o['schemas']) if 'schemas' in o else []),
]
def get_fields(o: OpenAPIObject) -> Sequence[str]:
return [
*(get_fields_from_components(o['components']) if 'components' in o else []),
]
恭喜!現在我們的資料集準備好了,
4.標識化
欄位名可能包含標點符號,如_和-符號,或大小寫為駝峰的單詞,我們可以把這些單詞切分,稱為標識,
下面的camel_case函式檢查駝峰命名,首先,它檢查是否有標點符號,如果是,那就不是駝峰命名,然后,它檢查單詞內部是否有大寫字母(不包括第一個和最后一個字符),
def camel_case(example):
if any(x in example for x in string.punctuation)==True:
return False
else:
if any(list(map(str.isupper, example[1:-1])))==True:
return True
else:
return False
下一個函式camel_case_split將駝峰單詞拆分為多個部分,為此,我們應該識別大寫字母并標記大小寫更改的位置,函式回傳拆分后的單詞串列,例如,欄位名BodyAsJson轉換為一個串列['Body','As','Json'],
def camel_case_split(word):
idx = list(map(str.isupper, word))
case_change = [0]
for (i, (x, y)) in enumerate(zip(idx, idx[1:])):
if x and not y:
case_change.append(i)
elif not x and y:
case_change.append(i+1)
case_change.append(len(word))
return [word[x:y] for x, y in zip(case_change, case_change[1:]) if x < y]
這個camel_case_split函式隨后用于以下標記化演算法,在這里,我們首先檢查單詞中是否有標點符號,然后,我們把這個詞分成幾部分,這些詞有可能是駝峰詞,如果是這樣的話,我們可以把它分成小塊,最后,拆分每個元素后,整個串列將轉換為小寫,
def tokenizer(mylist):
tokenized_list=[]
for word in mylist:
if '_' in word:
splitted_word=word.split('_')
for elem in splitted_word:
if camel_case(elem):
elem=camel_case_split(elem)
for el1 in elem:
tokenized_list.append(el1.lower())
else:
tokenized_list.append(elem.lower())
elif '-' in word:
hyp_word=word.split('-')
for i in hyp_word:
if camel_case(i):
i=camel_case_split(i)
for el2 in i:
tokenized_list.append(el2.lower())
else:
tokenized_list.append(i.lower())
elif camel_case(word):
word=camel_case_split(word)
for el in word:
tokenized_list.append(el.lower())
else:
tokenized_list.append(word.lower())
return(tokenized_list)
tokenizer(my_word)
5.創建欄位名的資料集
現在,讓我們用所有規范中的欄位名創建一個大資料集,
下面的dict_dataset函式獲取檔案名和路徑的串列,并打開每個規范檔案,對于每個檔案,get_field函式回傳欄位名的串列,某些欄位名稱可能在一個規范中重復,為了避免這種重復,讓我們使用list(dict.fromkeys(col))將串列中的欄位名串列轉換為字典,然后再回傳,然后我們可以將串列標識化,最后,我們創建一個以檔案名為鍵,以欄位名串列為值的字典,
def dict_dataset(datasets):
dataset_dict={}
for i in datasets:
with open(i, 'r') as foo:
col=algo.get_fields(yaml.safe_load(foo.read()))
if col:
mylist = list(dict.fromkeys(col))
tokenized_list=tokenizer(mylist)
dataset_dict.update({i: tokenized_list})
else:
continue
return (dataset_dict)
6.測驗嵌入
code2vec和GloVe
現在我們可以找出詞匯表外的單詞(未識別的單詞)并計算這些單詞在code2vec詞匯表中所占的百分比,以下代碼也適用于GloVe,
not_identified_c2v=[]
count_not_indent=[]
total_number=[]
for ds in test1:
count=0
for i in data[ds]:
if not i in model:
not_identified_c2v.append(i)
count+=1
count_not_indent.append(count)
total_number.append(len(data[ds]))
total_code2vec=sum(count_not_indent)/sum(total_number)*100
spaCy
spaCy詞匯表不同,因此我們需要相應地修改代碼:
not_identified_sp=[]
count_not_indent=[]
total_number=[]
for ds in test1:
count=0
for i in data[ds]:
f not i in nlp.vocab:
count+=1
not_identified_sp.append(i)
count_not_indent.append(count)
total_number.append(len(data[ds]))
total_spacy=sum(count_not_indent)/sum(total_number)*100
對于code2vec、glow和spaCy,未識別單詞的百分比分別為3.39、2.33和2.09,由于每個演算法的百分比相對較小且相似,因此我們可以進行另一個測驗,
首先,讓我們創建一個測驗字典,其中的單詞應該在所有API規范中都是相似的:
test_dictionary={'host': 'server',
'pragma': 'cache',
'id': 'uuid',
'user': 'client',
'limit': 'control',
'balance': 'amount',
'published': 'date',
'limit': 'dailylimit',
'ratelimit': 'rate',
'start': 'display',
'data': 'categories'}
對于GloVe和code2vec,我們可以使用gensim庫提供的similar_by_vector方法,spaCy還沒有實作這個方法,但是通過這個我們可以自己找到最相似的單詞,
為此,我們需要格式化輸入向量,以便在距離函式中使用,我們將在字典中創建每個鍵,并檢查對應的值是否在100個最相似的單詞中,
首先,我們將格式化詞匯表以便使用distance.cdist函式,這個函式計算詞匯表中每對向量之間的距離,然后,我們將從最小距離到最大距離對串列進行排序,并取前100個單詞,
from scipy.spatial import distance
for k, v in test_dictionary.items():
input_word = k
p = np.array([nlp.vocab[input_word].vector])
closest_index = distance.cdist(p, vectors)[0].argsort()[::-1][-100:]
word_id = [ids[closest_ind] for closest_ind in closest_index]
output_word = [nlp.vocab[i].text for i in word_id]
#output_word
list1=[j.lower() for j in output_word]
mylist = list(dict.fromkeys(list1))[:50]
count=0
if test_dictionary[k] in mylist:
count+=1
print(k,count, 'yes')
else:
print(k, 'no')
下表總結了結果,spaCy顯示單詞“client”位于單詞“user”的前100個最相似的單詞中,它對幾乎所有的OpenAPI規范都是有用的,并且可以用于將來OpenAPI規范相似性的分析,單詞“balance”的向量接近單詞“amount”的向量,我們發現它對支付API特別有用,

結論
我們已經為OpenAPI規范嘗試了三種不同的詞嵌入演算法,盡管這三個詞在這個資料集上都表現得很好,但是對最相似的單詞進行額外的比較表明spaCy對我們的情況更好,
spaCy比其他演算法更快,spaCy詞匯表的讀取速度比glow或code2vec詞匯表快5倍,然而,在使用該演算法時,缺少內置函式(如similar_by_vector和similar_word)是一個障礙,
另外,spaCy與我們的資料集很好地作業,這并不意味著spaCy對世界上的每個資料集都會更好,所以,請隨意嘗試為你自己的資料集嵌入不同的單詞,感謝你的閱讀!
原文鏈接:https://towardsdatascience.com/word-embeddings-with-code2vec-glove-and-spacy-5b26420bf632
歡迎關注磐創AI博客站:
http://panchuang.net/
sklearn機器學習中文官方檔案:
http://sklearn123.com/
歡迎關注磐創博客資源匯總站:
http://docs.panchuang.net/
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/79703.html
標籤:其他
上一篇:圖解隨機森林演算法
下一篇:voip集成測驗軟體下載
