Пошаговое сравнение слабого и полного надзора

В последние годы произошел значительный прогресс в обработке естественного языка (NLP) благодаря появлению моделей глубокого обучения. Реальные приложения, использующие НЛП, от интеллектуальных чат-ботов до автоматического извлечения данных из неструктурированных документов, становятся все более распространенными и приносят реальную ценность бизнесу для многих компаний. Тем не менее, эти модели по-прежнему требуют маркированных вручную обучающих данных, чтобы точно настроить их для конкретных случаев использования в бизнесе. Сбор этих данных может занять много месяцев, а их маркировка — еще больше, особенно если требуется эксперт в предметной области, а в тексте необходимо определить несколько классов. Как вы можете себе представить, это может стать настоящим барьером для многих предприятий, поскольку специалистов в данной области трудно найти и они дороги.

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

В этом руководстве мы создадим два обучающих набора данных из описаний работы: один создан со слабой маркировкой, а второй набор данных создан с помощью ручной маркировки с использованием UBIAI. Затем мы сравним производительность модели в задаче NER, которая направлена ​​на извлечение навыков, опыта, диплома и специальности из описания работы. Данные и записная книжка доступны в моем репозитории github.

Слабый надзор

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

Конвейеры слабого наблюдения состоят из трех компонентов: (1) определяемые пользователем функции маркировки и эвристические функции, (2) статистическая модель, которая принимает в качестве входных данных метки из функций и выводит вероятностные метки, и (3) модель машинного обучения, которая обучены на вероятностных обучающих метках из статистической модели.

Сквик

В этом уроке мы выполним слабую маркировку, используя библиотеку Skweak. Согласно создателям библиотеки [1]:

Skweak — это программный инструментарий на основе Python, который обеспечивает конкретное решение этой проблемы с использованием слабого контроля. skweak построен на очень простой идее: вместо аннотирования текстов вручную мы определяем набор функций маркировки для автоматической маркировки наших документов, а затем объединяем их результаты для получения помеченная версия нашего корпуса.

Чтобы узнать больше о библиотеке skweak, пожалуйста, прочитайте оригинальную статью skweak: Weak Supervision Made Easy for NLP».

Функции маркировки

Чтобы выполнить слабую маркировку, мы напишем набор функций, которые кодируют словари, шаблоны, базы знаний и правила, относящиеся к корпусу, который мы хотим пометить. В этом руководстве мы добавим функции, которые будут автоматически помечать сущности НАВЫКИ, ОПЫТ, ДИПЛОМ и ДИПЛОМ_МАЖОР из описания работы. После применения этих функций к немаркированным данным результаты будут объединены в единый слой вероятностных аннотаций с использованием статистической модели, предоставленной библиотекой skweak.

Сначала мы начнем с создания словаря навыков Skills_Data.json и добавим его в нашу функцию lf3, чтобы аннотировать сущность SKILLS. Словарь был получен из общедоступного набора данных.

#Create_Skills_Function_from_dictionary
import spacy, re
from skweak import heuristics, gazetteers, generative, utils
tries=gazetteers.extract_json_data('data/Skills_Data.json')
nlp=spacy.load('en_core_web_md' , disable=['ner'])
gazetteer = gazetteers.GazetteerAnnotator("SKILLS", tries)
lf3= gazetteers.GazetteerAnnotator("SKILLS", tries)

Для сущности ОПЫТ мы используем шаблон регулярного выражения, чтобы зафиксировать количество лет опыта:

#Create_Function_Foe_Experience_Detection(Use Regex)
def experience_detector (doc):
    expression=r'[0-9][+] years'
    for match in re.finditer(expression, doc.text):
        start, end = match.span()
        span = doc.char_span(start, end)
        if(span):
            yield span.start , span.end ,  "EXPERIENCE"
lf1 = heuristics.FunctionAnnotator("experience", experience_detector)

Для сущностей DIPLOMA и DPLOMA_MAJOR мы используем общедоступный набор данных от Kaggle и регулярное выражение:

with open('Diploma_Dic.json' , 'r' , encoding='UTF-8') as f :
    DIPLOMA=json.load(f)

print(len(DIPLOMA))
with open ('Diploma_Major_Dic.json' ,encoding='UTF-8') as f :
    DIPLOMA_MAJOR=json.load(f)
#Create Diploma_Function
def Diploma_fun(doc):
    for key in DIPLOMA:
                for match in re.finditer(key , doc.text , re.IGNORECASE):
                    start, end = match.span()
                    span = doc.char_span(start, end)
                    if(span):
                        yield (span.start , span.end ,  "DIPLOMA")
    
lf4 = heuristics.FunctionAnnotator("Diploma", Diploma_fun)
#Create_Diploma_Major_Function
def Diploma_major_fun(doc):  
    for key in DIPLOMA_MAJOR:
                for match in re.finditer(key , doc.text , re.IGNORECASE):
                    start, end = match.span()
                    span = doc.char_span(start, end)
                    if(span):
                        yield (span.start , span.end ,  "DIPLOMA_MAJOR")
    
lf2 = heuristics.FunctionAnnotator("Diploma_major", Diploma_major_fun)
#Create_Function_For_diploma_major(Use Regex)
def diploma_major_detector (doc):
    expression=re.compile(r"(^.*(Ph.D|MS|Master|BA|Bachelor|BS)\S*) in (\S*)")
    for match in re.finditer(expression, doc.text):
        start, end = match.span(3)
        span = doc.char_span(start, end)
        if(span):
            yield span.start , span.end ,  "DIPLOMA_MAJOR"

lf5 = heuristics.FunctionAnnotator("Diploma_major", diploma_major_detector)

Мы объединяем все функции вместе и используем статистическую модель Skweak, чтобы найти наилучшее согласие для автоматической маркировки данных.

#create_corpus_annotated_to_train_the_model
docs=[]
with open('Corpus.txt' , 'r') as f :
    data=f.readlines()
    for text in data:
        if (len(text) !=1):
            newtext=str(text)
            doc=nlp(newtext)
            doc=lf1(lf2(lf3(lf4(lf5(doc)))))
            print(doc.spans)
            docs.append(doc)
from skweak import aggregation
model = aggregation.HMM("hmm", ["DIPLOMA", "DIPLOMA_MAJOR" , "EXPERIENCE" , "SKILLS"])
docs = model.fit_and_aggregate(docs)

Наконец-то мы готовы обучить модель! Мы выбрали для обучения модель spaCy, так как она легко интегрируется с библиотекой skweak, но мы, конечно, можем использовать любую другую модель, например, трансформаторы. Аннотированные наборы данных доступны в репозитории github.

for doc in docs:
    doc.ents = doc.spans["hmm"]
utils.docbin_writer(docs, "train.spacy")
!python -m spacy train config.cfg --output ./output --paths.train train.spacy --paths.dev train.spacy

Теперь мы готовы запустить обучение с обоими наборами данных, полностью размеченными вручную и слабо размеченными, с одинаковым количеством документов:

Производительность модели набора данных с ручной маркировкой:

================================== Results ==================================

TOK     100.00
NER P   74.27 
NER R   80.10 
NER F   77.08 
SPEED   4506  


=============================== NER (per type) ===============================

                    P       R       F
DIPLOMA         85.71   66.67   75.00
DIPLOMA_MAJOR   33.33   16.67   22.22
EXPERIENCE      81.82   81.82   81.82
SKILLS          74.05   83.03   78.29

Производительность модели набора данных со слабыми метками:

================================== Results ==================================

TOK     100.00
NER P   31.78 
NER R   17.80 
NER F   22.82 
SPEED   2711  


=============================== NER (per type) ===============================

                     P       R       F
DIPLOMA          33.33   22.22   26.67
DIPLOMA_MAJOR    14.29   50.00   22.22
EXPERIENCE      100.00   27.27   42.86
SKILLS           33.77   15.76   21.49

Интересно, что производительность модели набора данных с ручной маркировкой значительно превосходит набор данных со слабой маркировкой с большим отрывом 0,77 для контролируемого по сравнению с 0,22 для слабого наблюдения. Если мы копнем глубже, мы обнаружим, что разрыв в производительности все еще сохраняется на уровне сущности (за исключением сущности ОПЫТ).

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

Заключение

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

Следите за нами в Твиттере @UBIAI5 или подпишитесь здесь!

Ссылки:

  1. https://github.com/NorskRegnesentral/skweak