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

И снова привет! Добро пожаловать в мою четвертую статью о машинном обучении. Недавно я реализовал проект, который считаю социально значимым. Я дам краткий обзор, что это такое, и сразу погрузлюсь в код :)

В этом проекте я применяю навыки инженерии данных для создания API (интерфейса прикладного программирования), который классифицирует сообщения о бедствиях из различных источников (Twitter, текстовые сообщения) по 36 категориям. Эта проблема классификации является разновидностью машинного обучения с учителем, поскольку модель учится классифицировать результат на основе предоставленных ей данных. Т.е. с чем связано сообщение: вода, еда, кров, деньги и т. д.? Причина в том, что когда случается бедствие, миллионы сообщений отправляются и публикуются в Твиттере, чтобы проинформировать об этом. Однако о стихийных бедствиях заботятся разные организации. Продовольствие может предлагать одна организация, а о тушении пожаров позаботится другая организация. Следовательно, полезность этого приложения будет заключаться в том, чтобы разделить эти сообщения на различные типы, чтобы можно было понять, какой тип помощи необходим для конкретного бедствия.

Структура проекта

Проект состоит из трех частей:

  1. Конвейер ETL
    Извлечение, преобразование и загрузка данных. Это связано с обработкой данных. А именно я загрузил, объединил и очистил набор данных сообщений и категорий. Я сохранил в базе данных SQLite, чтобы модель могла использовать его на следующем этапе обучения.
  2. Конвейер машинного обучения
    Конвейер машинного обучения предназначен для обучения модели и ее тестирования. Конвейер включает часть обработки текста, потому что она имеет дело с текстовыми источниками, как упоминалось в начале. Я также использовал GridSearchCV для дальнейшей настройки модели и сохранения ее как файла pickle.
  3. Веб-приложение Flask
    run.py process_data и train_classifier - это, по сути, конвейер ETL и конвейер ML, включенные в рабочее пространство терминала для обеспечения работы приложения.

Трубопровод ETL

В первой части проекта моя цель - извлечь нужные мне данные, произвести необходимые преобразования, чтобы потом использовать их при построении алгоритма. После того, как я взглянул на два набора данных, которые мне нужны: categories и messages, я объединил два набора данных, используя общий идентификатор.

# merge data sets 
df = messages.merge(categories, on = [‘id’]) 
df.head()

Затем я разделил категории на отдельные столбцы категорий и дал каждому столбцу отдельное имя категории.

# create a dataframe of the 36 individual category columns
categories = df['categories'].str.split(';', expand = True)
row = categories.head(1)
category_colnames = row.applymap(lambda x: x[:-2]).iloc[0, :].tolist()
# rename the columns of `categories`
categories.columns = category_colnames
categories.head()

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

for column in categories:
    # set each value to be the last character of the string
    categories[column] = categories[column].astype(str).str[-1]
    
    # convert column from string to numeric
    categories[column] = categories[column].astype(int)
categories.head()

После преобразования столбцов категорий я внес изменения во фрейм данных. Я заменил исходный столбец категории новыми столбцами категорий.

# drop the original categories column from `df`
df.drop('categories', axis = 1, inplace = True)
# concatenate the original dataframe with the new `categories` dataframe df = pd.concat([df, categories], axis = 1) 
df.head()

Проверив дубликаты в своих данных, я избавился от них.

# check number of duplicates
df[df.duplicated].shape
(170, 40)
# drop duplicates 
df.drop_duplicates(inplace = True)
# check number of duplicates 
df[df.duplicated].count()

Я наконец сохранил чистый набор данных в базе данных SQLite.

# Save the clean dataset into a sqlite database.
engine = create_engine('sqlite:///disaster.db')
df.to_sql('messages_disaster', engine, index=False)

ML конвейер

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

# load data from database
engine = create_engine('sqlite:///disaster.db')
df = pd.read_sql_table('messages_disaster', con = engine)
X = df['message']  
Y = df.drop(['message', 'genre', 'id', 'original'], axis = 1)
# Tokenization function to process text data.
def tokenize(text):
    tokens = word_tokenize(text)
    lemmatizer = WordNetLemmatizer()
    clean_tokens = []
    for tok in clean_tokens:
        clean_tok = lemmatizer.lemmatize(tok).lower().strip()
        clean_tokens.append(clean_tok)
    return clean_tokens

Конвейер машинного обучения будет использовать столбец message как входную и выходную классификацию по 36 категориям в наборе данных. Это проблема обработки естественного языка; т.е. обработка текста для извлечения смысла из сообщения. Разве это не потрясающе?

pipeline = Pipeline([
    ('vect', CountVectorizer()),
    ('tfidf', TfidfTransformer()),
    ('clf', MultiOutputClassifier(RandomForestClassifier()))
])

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

# Split data into train and test tests.
X_train, X_test, y_train, y_test = train_test_split(X,Y, test_size = 0.2, random_state = 45)
# Train the model.
pipeline.fit(X_train, y_train)

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

# Test the model and print the classification report for each of the 36 categories.
def performance(model, X_test, y_test):
    y_pred = model.predict(X_test)
    for i, col in enumerate(y_test):
        print(col)
        print(classification_report(y_test[col], y_pred[:, i]))
performance(pipeline, X_test, y_test)

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

# Improve the pipeline.
pipeline2 = Pipeline([
    ('vect', CountVectorizer()),
    ('best', TruncatedSVD()),
    ('tfidf', TfidfTransformer()),
    ('clf', MultiOutputClassifier(AdaBoostClassifier()))
])
# Train the adjusted pipeline. 
pipeline2.fit(X_train, y_train)
# Check the performance of the adjusted model.
performance(pipeline2, X_test, y_test)

Я пошел еще дальше и использовал другой набор параметров с определенным диапазоном значений. С помощью GridSearchCV модель выбирает лучшие параметры.

parameters2 = { 
              'tfidf__use_idf': (True, False), 
              'clf__estimator__n_estimators': [50, 100],
              'clf__estimator__learning_rate': [1,2] }
cv2 = GridSearchCV(pipeline2, param_grid=parameters2)
cv2.fit(X_train, y_train)
performance(cv2, X_test, y_test)

Создайте API

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

Заключительные слова

Если вы зашли так далеко, большое спасибо за чтение! Надеюсь, это даст вам представление о том, насколько полезным может быть машинное обучение. И насколько широк спектр его приложений. Мы можем буквально спасти жизни людей, зная, как обрабатывать текстовые данные и реализовывать модели. Кстати о моделях, вот полный проект на моем github.

Желаю вам всего наилучшего и оставайтесь счастливыми :)

P.S. Давайте подключимся к Linkedin!