Spacy предлагает надежный конвейер с открытым исходным кодом для обработки естественного языка. Итак, здесь я буду изучать Spacy для задачи NER, в конце этой статьи у нас будет модель NER, обученная на наборе данных о заболеваниях NCBI, готовая делать прогнозы по медицинской литературе !!!

Задача состоит в том, чтобы идентифицировать медицинские объекты, такие как («SpecificDisease», «DiseaseClass», «CompositeMention», «Modifier») из медицинской литературы.

ШАГИ:

  • Получите набор данных и проанализируйте его.
  • Предварительная обработка данных.
  • Преобразуйте набор данных в формат, который может обрабатывать Spacy.
  • Создайте пустой конвейер Spacy NER и создайте файл конфигурации Spacy.
  • Обучите модель.
  • Предсказать сущности !!! 😀

Шаг 1. Получите набор данных и проанализируйте его.

Набор данных: набор данных о заболеваниях NCBI.

Скачать: https://github.com/devang007/NER-Spacy/tree/main/dataset

Пример точки данных:

Шаг 2. Предварительная обработка данных

‹ Важное примечание ›

Мы будем очищать данные, удаляя ненужные специальные символы, обратите внимание, что мы не будем удалять цифры из текста, так как в данном случае это полезная информация,

ex дефицит C7 , если мы удалим цифры из этого заболевания, мы рискуем потерять важную информацию, нам нужно найти способ, чтобы числа также были в тексте. Может быть много способов справиться с этим, один из способов, с которым я столкнулся, читая исследовательскую работу по связанной проблеме, заключается в том, что мы можем заменить целые числа строкой «num». В этой статье я не исследую эту возможность для первой версии модели, вы можете попробовать и поделиться своими наблюдениями!!!

data = []

for line in lines:
    matches = re.findall(r'<category=\"(\w+)\">(.*?)<\/category>',line)     
    dic = {}
    
    dic['text'] = line

    for key,value in matches:
        if key in dic:
            value = re.sub(r'[^a-zA-Z0-9]',' ',value)
            dic[key].append(value)
        else :
            value = re.sub(r'[^a-zA-Z0-9]',' ',value)
            dic[key] = [value]
    data.append(dic)

df=pd.DataFrame(data,columns=['text','SpecificDisease','DiseaseClass','CompositeMention','Modifier'])

df.to_csv('./parsedNCBIDataset.csv')

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

Шаг 3. Преобразуйте набор данных в формат, который может обрабатывать Spacy.

Ниже приведен формат, на который мы ориентируемся.

#spacy ner sample input format : 

# TRAIN_DATA =[ ("Pizza is a common fast food.", {"entities": [(0, 5, "FOOD")]}),
              ("Pasta is an italian recipe", {"entities": [(0, 5, "FOOD")]})]

#create the data in above format.

def cleanText(text):
    text = re.sub(r'<category=\"(\w+)\">',' ',text)
    text = re.sub(r'</category>',' ',text)
    text = re.sub(r'[0-9]{4,}',' ',text)
    text = re.sub(r'\t',' ',text)
    text = re.sub(r'[^a-zA-Z0-9]',' ',text)
    text=text.strip()
    return text
#currently not eliminating numbers, we can replace it with "num" will do it later

def createDataPoint(paragraph,names,entity):
   
    results = []    
    for substring in names:
        start_positions = []
        end_positions = []
        start = -1

        while True:
            start = paragraph.find(substring, start + 1)
            if start == -1:
                break
            end = start + len(substring)
            start_positions.append(start)
            end_positions.append(end)

            
        for i in range(len(start_positions)):
            start_position = start_positions[i]
            end_position = end_positions[i]
            result = (start_position, end_position, entity)
            results.append(result)
    return results

datapoints = []

for i in range(len(df)):
    print("*"*50)
    text = cleanText(df.iloc[i]['text'])
    print(text)
    dic = {
        "text" : text,
        "entities" : {}
    }
classes = ['SpecificDisease','DiseaseClass','CompositeMention','Modifier']
    results = []
    for c in classes:
        names = df.iloc[i][c]
        entity = c
        if names != None and not names!=names:
            names = re.sub(r'[\']',"\"",names)
            names  = json.loads(names)
            names = list(set(names))
            r = createDataPoint(text,names,c)
            if r is None:
                continue
            results.extend(r)
        else:
            print("none")
    
    ent = {'entities' : results}
    dic['entities'] = json.dumps(ent)
    
    datapoints.append(dic)

datapointsDf = pd.DataFrame(datapoints,columns=['text','entities'])

datapointsDf.to_csv('traindata.csv',index=False)

После вышеуказанного шага мы переходим к формату данных ниже.

Подготовка данных для обучения

data = pd.read_csv('traindata.csv')
#data format = [(text,entities)]

train_data  = [] 

for i in range(len(data)):
    dic = json.loads(data.iloc[i].entities)
    toTup = [tuple(lst) for lst in dic['entities']]
    tup = {'text' : data.iloc[i].text ,'entities' : toTup }
    train_data.append(tup)

Теперь у нас есть готовые данные, и мы можем приступить к созданию пользовательского конвейера NER.

Шаг 4.Создайте пустой конвейер spacy NER и создайте файл конфигурации spacy.

У Spacy есть отличная документация по конвейерам обучения, в нашем случае мы будем генерировать файл конфигурации для нашей модели из пользовательского интерфейса spacy.

Генератор конфигурационных файлов: https://spacy.io/usage/training

Вышеупомянутые варианты выбора предназначены для пользовательского конвейера NER в пространстве. Если вы используете машину с графическим процессором, выберите GPU, в противном случае набор данных невелик, поэтому мы также можем использовать ЦП, поскольку обучение с 10 эпохами не займет много времени. Но GPU всегда лучше, особенно когда вы можете получить его бесплатно в Google Collab 😀.

Итак, как только мы загрузили сгенерированный файл конфигурации, мы можем сразу перейти к построению модели.

Мы будем использовать пустую модель, в основном модель не будет поставляться с предварительно обученными весами, мы будем использовать предварительно обученные модели в следующих частях, а пока мы будем обучать простую ванильную модель.

nlp = spacy.blank("en") # load a new spacy model
doc_bin = DocBin() # create a DocBin object

DocBin используется для преобразования данных в формат файла .spacy, поэтому, как только структура данных будет в порядке, мы можем сохранить файл .spacy с объектом docbin,

Но перед сохранением (извините!!!) последний шаг подготовки (обещаю!!) и самый важный шаг остался,

Это удаление перекрывающихся пролетов,

Промежутки: span означает последовательность слов документа,

Так, например, Билл Гейтс является основателем Microsoft. Здесь Билл Гейтс — это пролет.

Итак, для приведенного выше примера пусть сущности будут следующими:

«Человек»: 0-10 (Билл Гейтс)

«Организация»: 0–10 (Билл Гейтс) Это просто пример!!

Итак, сущности перекрываются, поэтому модель неоднозначно понимает, какая сущность верна, поэтому нам нужно удалить такие экземпляры.

Вот как мы можем удалить их,

for training_example  in tqdm(train_data): 
    text = training_example['text']
    labels = training_example['entities']
    doc = nlp.make_doc(text) 
    ents = []
    for start, end, label in labels:
        span = doc.char_span(start, end, label=label, alignment_mode="contract")
        if span is None:
            print("Skipping entity")
        else:
            ents.append(span)
    filtered_ents = filter_spans(ents)
    doc.ents = filtered_ents 
    doc_bin.add(doc)

doc_bin.to_disk("training_data.spacy") # save the docbin object

Шаг 5.Обучите модель.

Итак, последняя и веселая часть процесса здесь,

Нам просто нужно инициализировать файл конфигурации, который мы только что загрузили из spacy, в соответствии с нашими потребностями,

python -m spacy init fill-config base_config.cfg config.cfg

И, наконец, обучите свою модель, предоставив обучающий файл и выходное местоположение.

python -m spacy train config.cfg --output ./ --paths.train ./training_data.spacy --paths.dev ./training_data.spacy

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

Шаг 6. Прогнозировать объекты!!! 😀

Время предсказаний!!

Мы будем использовать библиотеку displacy, чтобы прогнозы выглядели круто!!

def findEntity(text):
    nlp_ner = spacy.load("model-best")  # location of your created model

    doc = nlp_ner(text)

    colors = {"SpecificDisease": "#F67DE3", "DiseaseClass": "#7DF6D9", "CompositeMention":"#FF3355","Modifier":"#68FF33"}
    options = {"colors": colors} 

    spacy.displacy.render(doc, style="ent", options= options, jupyter=True)

text = '''

'''
text = cleanText(text)
findEntity(text)

Заключение

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

Оформить заказ Полный блокнот Python