Встраивание слов с помощью code2vec, свечения и spaCy

машинное обучение глубокое обучение компьютерное зрение NLP

Автор|Мария Малицкая Компилировать|ВКонтакте Источник | К науке о данных

Эффективным способом улучшения моделей машинного обучения является использование встраивания слов. Используя встраивание слов, вы можете зафиксировать контекст слов в документе, а затем найти семантическое и грамматическое сходство.

В этом посте мы обсудим необычное применение методов встраивания слов. Мы постараемся найти в нем лучшие методы встраивания слов, используя спецификацию OpenAPI в качестве набора данных. В качестве примера спецификации OpenAPI будем использовать спецификации OpenAPI (swagger.IO/конкретная ATI…

Самая большая проблема заключается в том, что спецификация OpenAPI не является ни естественным языком, ни кодом. Но это также означает, что мы можем использовать любую доступную модель встраивания. В этом эксперименте мы исследуем трех возможных кандидатов: code2vec, свечение и spaCy.

code2vec — это нейронная модель, которая изучает аналогии, связанные с исходным кодом. Модель обучается на базе данных Java-кода, но вы можете применить ее к любому коду.

И Перчатка. GloVe — широко используемый алгоритм обработки естественного языка. Обучался на Wikipedia и Gigawords.

Наконец, у нас есть spaCy. Хотя spaCy является относительно недавней разработкой, алгоритм уже известен как самый быстрый в мире встраивание слов.

Давайте посмотрим, какой из этих алгоритмов лучше работает с набором данных OpenAPI, а какой работает быстрее для спецификации OpenAPI.Я разделил этот пост на шесть частей, каждая с примерами кода и некоторыми вариантами использования в будущем, а также заключение.

  1. Скачать набор данных

  2. Скачать глоссарий

  3. Извлечь имена полей

  4. токенизация

  5. Создайте набор данных с именами полей

  6. тестовое встраивание

  7. в заключении

Теперь мы можем начать.

1. Загрузите набор данных

Во-первых, нам нужно скачать всю базу данных apis-guru:APIs.guru/.

Вы заметите, что большинство спецификаций apis-guru представлены в формате Swagger 2.0. Однако последней версией спецификации OpenAPI является OpenAPI 3.0.

Итак, давайте конвертируем весь набор данных в этот формат с помощью скрипта Unmock! Вы можете сделать это, следуя инструкциям в файле README для сценария Unmock openapi:GitHub.com/Какую сумму посмотреть/UN…

Это может занять некоторое время, и в итоге вы получите большой набор данных.

2. Загрузите глоссарий

code2vec

  1. Загрузите модель со страницы code2vec GitHub и следуйте инструкциям в разделе быстрого запуска.

  2. Загружается с помощью библиотеки gensim.

model = word2vec.load_word2vec_format(vectors_text_path, binary=False)

GloVe

  1. Загрузите глоссарий GloVe с веб-сайта. Мы выбрали самый большой, потому что тогда он с большей вероятностью найдет все наши слова. Вы можете выбрать, откуда его скачать, но для удобства лучше всего хранить его в рабочем каталоге.

  2. Вручную загрузите глоссарий 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/