Автор|Мария Малицкая Компилировать|ВКонтакте Источник | К науке о данных
Эффективным способом улучшения моделей машинного обучения является использование встраивания слов. Используя встраивание слов, вы можете зафиксировать контекст слов в документе, а затем найти семантическое и грамматическое сходство.
В этом посте мы обсудим необычное применение методов встраивания слов. Мы постараемся найти в нем лучшие методы встраивания слов, используя спецификацию OpenAPI в качестве набора данных. В качестве примера спецификации OpenAPI будем использовать спецификации OpenAPI (swagger.IO/конкретная ATI…
Самая большая проблема заключается в том, что спецификация OpenAPI не является ни естественным языком, ни кодом. Но это также означает, что мы можем использовать любую доступную модель встраивания. В этом эксперименте мы исследуем трех возможных кандидатов: code2vec, свечение и spaCy.
code2vec — это нейронная модель, которая изучает аналогии, связанные с исходным кодом. Модель обучается на базе данных Java-кода, но вы можете применить ее к любому коду.
И Перчатка. GloVe — широко используемый алгоритм обработки естественного языка. Обучался на Wikipedia и Gigawords.
Наконец, у нас есть spaCy. Хотя spaCy является относительно недавней разработкой, алгоритм уже известен как самый быстрый в мире встраивание слов.
Давайте посмотрим, какой из этих алгоритмов лучше работает с набором данных OpenAPI, а какой работает быстрее для спецификации OpenAPI.Я разделил этот пост на шесть частей, каждая с примерами кода и некоторыми вариантами использования в будущем, а также заключение.
-
Скачать набор данных
-
Скачать глоссарий
-
Извлечь имена полей
-
токенизация
-
Создайте набор данных с именами полей
-
тестовое встраивание
-
в заключении
Теперь мы можем начать.
1. Загрузите набор данных
Во-первых, нам нужно скачать всю базу данных apis-guru:APIs.guru/.
Вы заметите, что большинство спецификаций apis-guru представлены в формате Swagger 2.0. Однако последней версией спецификации OpenAPI является OpenAPI 3.0.
Итак, давайте конвертируем весь набор данных в этот формат с помощью скрипта Unmock! Вы можете сделать это, следуя инструкциям в файле README для сценария Unmock openapi:GitHub.com/Какую сумму посмотреть/UN…
Это может занять некоторое время, и в итоге вы получите большой набор данных.
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. Давайте определим функцию 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 ниже проверяет верблюжий регистр. Во-первых, он проверяет пунктуацию. Если да, то это не CamelCase. Затем он проверяет, есть ли заглавные буквы внутри слова (исключая первый и последний символы).
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 и перчатка
Теперь мы можем найти слова вне словаря (нераспознанные слова) и подсчитать процент этих слов в словаре 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
Процент нераспознанных слов составляет 3,39, 2,33 и 2,09 для code2vec, Glow и SpaCy соответственно. Поскольку проценты для каждого алгоритма относительно малы и схожи, мы можем запустить еще один тест.
Во-первых, давайте создадим тестовый словарь, слова которого должны быть похожи во всех спецификациях 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 мы можем использовать метод Similar_by_vector, предоставляемый библиотекой gensim. 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 показывает, что слово «клиент» входит в первую сотню слов, наиболее похожих на слово «пользователь». Он полезен почти для всех спецификаций OpenAPI и может использоваться для будущего анализа подобия спецификаций OpenAPI. Вектор слова «остаток» близок к вектору слова «сумма». Мы нашли его особенно полезным для платежных API.
в заключении
Мы попробовали три разных алгоритма встраивания слов для спецификации OpenAPI. Хотя все три слова хорошо работают в этом наборе данных, дополнительное сравнение наиболее похожих слов показывает, что в нашем случае лучше работает spaCy.
spaCy быстрее других алгоритмов. Словарь spaCy читается в 5 раз быстрее, чем словарь glow или code2vec. Однако отсутствие встроенных функций, таких как Similar_by_vector и Similar_word, является помехой при использовании этого алгоритма.
Кроме того, spaCy хорошо работает с нашим набором данных, что не означает, что spaCy будет лучше для любого набора данных в мире. Итак, не стесняйтесь экспериментировать с различными вложениями слов для вашего собственного набора данных, спасибо за чтение!
Оригинальная ссылка:к data science.com/word-embed…
Добро пожаловать на сайт блога Panchuang AI:panchuang.net/
sklearn машинное обучение китайские официальные документы:sklearn123.com/
Добро пожаловать на станцию сводки ресурсов блога Panchuang:docs.panchuang.net/