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

В общем случае схема работы машинного обучения такова:

  1. Создают программу используя специальные библиотеки

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

  3. Сохраняют и используют обученную модель

Я в своем примере совместил обучение программы вместе с ее тестированием, поэтому при каждом запуске она сперва обучается (что занимает некоторое время, не думайте что программа зависла).

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

Итак, мы создадим программу которая пытается угадать какой тип контента нужно найти для пользователя, в ответ на его вопрос. Например, если юзер спросит "Кто такой Виктор Цой", программа должна ответить "Информация о личности". Она не будет отвечать на вопрос, просто классифицирует вопрос в определенную категорию. Обучающий набор данных очень маленький поэтому тестировать будем на более-менее похожих вопросах.

Создаём набор данных для обучения

Сперва создадим набор данных для обучения. У меня это обычный текстовый файл в котором лежат строки, разделенные значком @. Назовите файл model.txt и киньте рядом со скриптом. Используйте кодировку UTF-8.

Что такое патока @ Информация о предмете
Где живут медведи @ Местоположение
Почему небо голубое @ Информация о предмете
Как сварить борщ @ Инструкция
Как научиться летать @ Инструкция
Как рано просыпаться @ Инструкция
Как обрести счастье @ Инструкция
Как убить таракана @ Инструкция
Сколько звезд на небе @ Количество
Какая высота у Эйфелевой башни @ Количество
Почему идет дождь @ Информация о предмете
В каких фильмах играл Ди Каприо @ Информация о предмете
Когда родился Эйнштейн @ Дата рождения
Когда началась Первая мировая война @ Дата
В каком году был построен Титаник @ Дата
Когда начал править Иван IV @ Дата
Когда была битва под Аустерлицем @ Дата
В каких годах была «Семилетняя война» @ Дата
Когда начал править Наполеон Бонапарт @ Дата
Когда закончилось правление Наполеона Бонапарта @ Дата
В каких годах правил князь Святослав @ Дата
В каком году умер Ярослав Мудрый @ Дата
В каком году появляется первое упоминание о Москве @ Дата
В каком году был основан Санкт-Петербург @ Дата
Когда было Ледовое побоище @ Дата
Когда была Куликовская битва @ Дата
В каком году начали печатать книги на Руси @ Дата
Когда была Полтавская битва @ Дата
В каком году закончилась Первая мировая война @ Дата
В каком году был заключен Тильзитский мир @ Дата
В каком году были написаны «Отцы и дети» @ Дата
Когда отмечается День Учителя @ Дата
Когда отмечается День Матери @ Дата
Когда отменили крепостное право @ Дата
Что произошло 12 июня 1812 @ Дата
Что произошло 20 ноября 1805 @ Дата
Что произошло 6 июня 1799 @ Дата
Что произошло 1 апреля 1809 @ Дата
Какой город начинает упоминатся с 1147 грода @ Дата
Кто такой Эйнштейн @ Информация о личности
Что такое «дождь» @ Информация о предмете
Кто основал Санкт-Петербург @ Имя
Кто такой Христофор Колумб @ Информация о личности
Как звали Колумба @ Имя
Кто написал произведение «Преступление и наказание» @ Имя
Кто написал стихотворение «Тройка» @ Имя
Какова средняя глубина Тихого океана @ Количество
Какова высота Биг Бена @ Количество
Где находится Сиднейская Опера @ Местоположение
Где находится самое большое озеро @ Местоположение
В какой стране столицей является город Москва @ Местоположение

Установка необходимых библиотек

Нам понадобится библиотека PyStemmer. Установить её можно так

pip3 install pystemmer

Пишем программу

Теперь покажу сам скрипт для классификации вопросов.

import sys
import numpy as np
import pickle
import re
from Stemmer import Stemmer
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import SGDClassifier
from sklearn.pipeline import Pipeline
from sklearn.metrics import accuracy_score

# очистка текста с помощью regexp приведение слов в инфинитив и нижний регистр, замена цифр

def text_cleaner(text):
    text = text.lower() # приведение в lowercase 
    stemmer = Stemmer('russian')
    text = ' '.join( stemmer.stemWords( text.split() ) ) 
    text = re.sub( r'\b\d+\b', ' digit ', text ) # замена цифр 
    return  text 


# загрузка данных из файла model.txt

def load_data():   
    data = {'text':[],'tag':[]}
    for line in open('model.txt'):
        if not('#' in line):
            row = line.split("@") 
            data['text'] += [row[0]]
            data['tag'] += [row[1]]
    return data

# Обучение 

def train_test_split(data, validation_split = 0.1):
    sz = len(data['text'])
    indices = np.arange(sz)
    np.random.shuffle(indices)

    X = [data['text'][i] for i in indices]
    Y = [data['tag'][i] for i in indices]
    nb_validation_samples = int( validation_split * sz )

    return { 
        'train': {'x': X[:-nb_validation_samples], 'y': Y[:-nb_validation_samples]},
        'test': {'x': X[-nb_validation_samples:], 'y': Y[-nb_validation_samples:]}
    }


# - - - -

def openai():
    data = load_data()
    D = train_test_split(data)
    text_clf = Pipeline([
                    ('tfidf', TfidfVectorizer()),
                    ('clf', SGDClassifier(loss='hinge')),
                    ])
    text_clf.fit(D['train']['x'], D['train']['y'])
    predicted = text_clf.predict( D['train']['x'] )
    
# Начало тестирования программы
    
    z = input("Введите вопрос без знака вопроса на конце: ")
    zz = []
    zz.append(z)
    predicted = text_clf.predict(zz) 
    print(predicted[0])
    
# - - - -
if __name__ == '__main__':
    sys.exit(openai())

Давайте запустим нашу программу, подождем пока она обучится (пару минут) и предложит нам ввести вопрос. Введем вопрос которого нет в обучающем файле - "Кто придумал ракету". Писать нужно без знака вопроса на конце. В ответ программа выдаст "Имя", значит она определила что наш вопрос подразумевает ответ в котором должно быть чье-то имя. Заметьте, данного вопроса не было в файле model.txt но программа безошибочно определила что мы имеем ввиду, исходя из обучающей выборки.

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

Комментарии (19)


  1. sunnybear
    04.01.2023 00:23
    +3

    Почему просто по вопросительному слову не давать 95%+ точность?


  1. pharo
    04.01.2023 00:43

    На какие вопросы подходящие под шаблон из обучающей выборки можно получить более двух классификаторов?
    Или такое невозможно из представленной модели вопросов?


  1. Maksim-Burtsev
    04.01.2023 01:22

    if not('#' in line):
    

    А где у вас решётка к model.txt?


    1. ooneon
      04.01.2023 11:22

      Думаю этот код нужен, чтобы пропускать закомментированные строки.


      1. rSedoy
        04.01.2023 11:30

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


      1. PrinceKorwin
        04.01.2023 13:05

        Я не совсем программист, но точно эта строка отсечет комментарии без false positive? Ну там пробелы перед # или использование символа # внутри строкового литерала.


        1. IvaYan
          04.01.2023 13:12

          Оно отбросит вообще все строки, в которых есть # в любом виде, независимо от его расположения в строке.


  1. pharo
    04.01.2023 02:15

    Упомянутая в статье библиотека Stemmer на Си и других языках.

    Сайт: snowballstem.org
    Проект на Github


  1. acyp
    04.01.2023 06:26
    +1

    Допускаю, что я не "настоящий сварщик", но все-таки. Функция text_cleaner объявлена, а использования нет. Вкупе с отсутсвующим в модельном файле диезом создается впечатление то-ли заготовки, то-ли копипасты без понимания сути.


  1. rSedoy
    04.01.2023 07:58
    +2

    Автор, фу быть таким. Чуток погуглил (и то не факт что это оригинал), https://telegra.ph/Pishem-prostuyu-nejroset-12-25 даже, прости, господи, 3 года назад на майл-ответах https://otvet.mail.ru/question/216434772


    1. Bitumok
      04.01.2023 20:38
      +1

      Судя по статье из первой ссылки, там тоже автор не особо разбирался...


  1. IvaYan
    04.01.2023 10:45
    +1

    А зачем свой train_test_split, если он уже есть в sklearn и гораздо более навороченный?


  1. fry404
    04.01.2023 11:23

    Какие еще библиотеки необходимы, код не удалось запустить


    1. Lord_of_Rings Автор
      04.01.2023 11:23

      Какую ошибку выдаёт?


      1. rSedoy
        04.01.2023 11:34
        +3

        будут комментарии на тему, почему чужое выдаешь за свое?


        1. fry404
          04.01.2023 21:35

          Using cached PyStemmer-2.2.0.tar.gz (698 kB)
          Preparing metadata (setup.py) ... error
          error: subprocess-exited-with-error

          × python setup.py egg_info did not run successfully.
          │ exit code: 1
          ╰─> [10 lines of output]
          Traceback (most recent call last):
          File "", line 2, in
          File "", line 34, in
          File "C:\Users_\AppData\Local\Temp\pip-install-400byijh\pystemmer_3d70e74fe4dd4f38a92b3a86a3dda42b\setup.py", line 199, in
          ['src/Stemmer.pyx'] + list(LIBRARY_SOURCE_CODE.source_code_paths()),
          File "C:\Users_\AppData\Local\Temp\pip-install-400byijh\pystemmer_3d70e74fe4dd4f38a92b3a86a3dda42b\setup.py", line 85, in source_code_paths
          for line in self.iter_manifest_lines():
          File "C:\Users_\AppData\Local\Temp\pip-install-400byijh\pystemmer_3d70e74fe4dd4f38a92b3a86a3dda42b\setup.py", line 74, in iter_manifest_lines
          with open(self.manifest_file_path) as file:
          FileNotFoundError: [Errno 2] No such file or directory: 'libstemmer_c-2.2.0\mkinc_utf8.mak'
          [end of output]

          note: This error originates from a subprocess, and is likely not a problem with pip.
          error: metadata-generation-failed

          × Encountered error while generating package metadata.
          ╰─> See above for output.

          note: This is an issue with the package mentioned above, not pip.
          hint: See above for details.


  1. IvaYan
    04.01.2023 13:17
    +2

    Вообще, статья странная. Не упомянуто, какая именно модель используется, что вообще происходит, как происходит классификация, и почему именно так. @Lord_of_Rings, не поделитесь подробностями?


  1. Bitumok
    04.01.2023 20:17
    +4

    Критиковать, конечно, легче, чем статьи писать, но мне кажется, что данная статья, мало того, что не полезная, так еще и откровенно вредная из-за кривого кода.

    Если учесть наличие плагиата без указания источника, то совсем плохо.


  1. jandevel
    04.01.2023 22:53

    В статье продемонстрирован классический пример, где ML не нужен в принципе. Обычный эвристический алгоритм даст идеальное качество.