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

Целта ми беше да мога да тествам различни комбинации и да видя дали мога да постигна прилична точност. Научих се да разработвам леки уеб приложения, използвайки streamlit и python. Ако не знаете нищо за streamlit, препоръчвам ви да разгледате моя „урок“ за визуализация на данни, за да започнете.

Пълният код на този урок, както и данните могат да бъдат намерени в моя github.

Настройване на streamlit и създаване на приложението

Ако никога не сте го правили, можете да инсталирате streamlit с помощта на тази проста команда:

$ pip install streamlit

Създайте нов файл в папката на вашето приложение, наименувайте го classification.py и импортирайте следните библиотеки.

import streamlit as st
import pandas as pd
import numpy as np
import os
from sklearn.preprocessing import OneHotEncoder, StandardScaler, MinMaxScaler, RobustScaler
from sklearn.impute import SimpleImputer
from sklearn.pipeline import make_pipeline, Pipeline
from sklearn.compose import make_column_transformer
from sklearn.model_selection import KFold, cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier

Импортиране на данните

Нека сега импортираме данните. За този пример ще използваме набора от данни на Титаник (щракнете върху връзките, за да изтеглите данните). Ще създадем функция за зареждане на данните в приложението. Трябва да поставим csv файла в същата папка като classification.py

#Loading the data
def get_data_titanic():
    return pd.read_csv(os.path.join(os.getcwd(), 'titanic.csv'))

Да видим дали всичко работи добре, като заредим данните и покажем рамката с данни:

#configuration of the page
st.set_page_config(layout="wide")
st.title('Classification exploratory tool')
st.markdown("""
This app allows you to test different machine learning
algorithms and combinations of preprocessing techniques
to classify passengers from the Titanic dataset!
""")
#load the data
df = get_data_titanic()
st.header('Original dataset')
st.write(df)

Streamlit предлага различни начини за показване на текст: st.title, st.header и st.write са доста ясни; st.markdown ви позволява да форматирате текста с помощта на синтаксиса на markdown.

Стартирайте приложението си

Въведете следващата команда в терминал и тя трябва да отвори уеб браузър с вашето приложение.

$ streamlit run classification.py

Разделяне на колоните в техните категории

Преди да изградим нашия инструмент, трябва да категоризираме всяка колона и да подготвим данните за обучение. Категориите са числови, категорични, липсващи, падащи и целеви.

target_selected = 'Survived'
cat_cols_missing = ['Embarked']
num_cols_missing = ['Age']
cat_cols = ['Pclass', 'SibSp', 'Parch', 'Sex']
num_cols = ['Fare']
drop_cols = ['PassengerId']
X = df.drop(columns = target_selected)
y = df[target_selected].values.ravel()

Сега данните са готови за предварителна обработка. Ще създадем различни стъпки за предварителна обработка в зависимост от категорията.

Изграждане на тръбопровода за предварителна обработка

В машинното обучение „тръбопроводът“ е последователност от трансформации на набор от данни. Конвейерът обикновено завършва с оценител, който прави прогноза. Използването на конвейер за предварителна обработка и прогнозиране има много предимства:

  • Четливост и организация на кода
  • Само веднъж трябва да извикате годни и трансформирани
  • Използване на GridSearch за тестване на различни параметри и намиране на най-оптимизирания конвейер

В нашия случай тръбопроводът за предварителна обработка ще се състои от три основни трансформации:

  • Импутация за попълване на колони с липсващи данни
  • OneHotEncoding за трансформиране на категориални данни в числови данни
  • „Мащабиране“ за стандартизиране на данните и ограничаване на ефекта от отклоненията

Създаване на приспособления за персонализиране на конвейера за предварителна обработка

В streamlit можем да създаваме обекти или джаджи за взаимодействие с потребителя. Това ще му позволи да избере, в нашия случай, типа импулс, енкодер и мащабиране, които да се използват в тръбопровода за предварителна обработка.

#Sidebar
st.sidebar.title('Preprocessing')
cat_imputer_selected = st.sidebar.selectbox('Handling categorical missing values', ['None', 'Most frequent value'])
num_imputer_selected = st.sidebar.selectbox('Handling numerical missing values', ['None', 'Median', 'Mean'])
encoder_selected = st.sidebar.selectbox('Encoding categorical values', ['None', 'OneHotEncoder'])
scaler_selected = st.sidebar.selectbox('Scaling', ['None', 'Standard scaler', 'MinMax scaler', 'Robust scaler'])

Създадохме четири обекта в кутия за избор. Сега те се показват отстрани на нашето приложение. Сега трябва да вземем предвид избраните стъпки за предварителна обработка и да ги добавим към конвейера. За да направим това, ще създадем функции, които ще върнат импулс, скалер или енкодер от sklearn. Тези три функции са доста ясни. Не забравяйте да поставите тези функции в началото на кода, например точно след функцията get_data_titanic.

def get_imputer(imputer):
    if imputer == 'None':
        return 'drop'
    if imputer == 'Most frequent value':
        return SimpleImputer(strategy='most_frequent', missing_values=np.nan)
    if imputer == 'Mean':
        return SimpleImputer(strategy='mean', missing_values=np.nan)
    if imputer == 'Median':
        return SimpleImputer(strategy='median', missing_values=np.nan)
def get_encoder(encoder):
    if encoder == 'None':
        return 'drop'
    if encoder == 'OneHotEncoder':
        return OneHotEncoder(handle_unknown='ignore', sparse=False)
def get_scaler(scaler):
    if scaler == 'None':
        return 'passthrough'
    if scaler == 'Standard scaler':
        return StandardScaler()
    if scaler == 'MinMax scaler':
        return MinMaxScaler()
    if scaler == 'Robust scaler':
        return RobustScaler()

Нуждаем се също от две други функции за обработка на колони, които се нуждаят от две стъпки за предварителна обработка. В нашия случай категоричните колони с липсващи данни може да се нуждаят от функции за импулсиране и енкодер. Нека напишем кода за тези функции, една за категорични данни и една за числови данни.

def get_pip_mis_num(imputer, scaler):
    if imputer == 'None':
        return 'drop'
    pipeline = make_pipeline(get_imputer(imputer))
    pipeline.steps.append(('scaling', get_scaler(scaler)))
    return pipeline
def get_pip_mis_cat(imputer, encoder):
    if imputer == 'None' or encoder == 'None':
        return 'drop'
    pipeline = make_pipeline(get_imputer(imputer))
    pipeline.steps.append(('encoding', get_encoder(encoder)))
    return pipeline

Тези две функции могат да върнат конвейер в две стъпки, ако имаме нужда от него.

Тръбопроводът за предварителна обработка

Нека най-накрая изградим тръбопровода за предварителна обработка. Първо създаваме трансформатор на колони, за да разграничим стъпките на предварителна обработка в зависимост от колоните.

preprocessing = make_column_transformer( 
(get_pip_mis_cat(cat_imputer_selected, encoder_selected) , cat_cols_missing),
(get_pip_mis_num(num_imputer_selected, scaler_selected) , num_cols_missing),
(get_encoder(encoder_selected), cat_cols),
(get_scaler(scaler_selected), num_cols),
("drop" , drop_cols)
)
preprocessing_pipeline = Pipeline([
    ('preprocessing' , preprocessing)
])

Успешно изградихме тръбопровода за предварителна обработка. Сега ще съобразим този тръбопровод с данните и ще трансформираме данните. Нека го видим в действие.

preprocessing_pipeline.fit(X)
X_preprocessed = preprocessing_pipeline.transform(X)
st.header('Preprocessed dataset')
st.write(X_preprocessed)

Чрез избиране на импулс, енкодер и скалер бихме могли да получим резултат като този:

Предварително обработените данни не съдържат никакви категорични или текстови данни. Той е готов да бъде предаден на алгоритми за машинно обучение за прогнозиране.

Изграждане на окончателния тръбопровод за прогнозиране

Можем да дадем избора на класификатора на потребителя и да добавим избрания модел към нашия окончателен конвейер.

st.sidebar.title('Model selection')
classifier_list = ['Logistic regression', 'Support vector', 'K nearest neighbors', 'Random forest']
classifier_selected = st.sidebar.selectbox('', classifier_list)
pipeline = Pipeline([
    ('preprocessing' , preprocessing),
    ('ml', get_ml_algorithm(classifier_selected))
])

Както можете да видите в кода по-горе, липсва ни функцията get_ml_algorithm. Тази функция ще изглежда много като функциите, които създадохме по-рано за импутера, енкодера и скалера за тръбопровода за предварителна обработка. Нека напишем тази функция.

def get_ml_algorithm(algorithm):
    if algorithm == 'Logistic regression':
        return LogisticRegression()
    if algorithm == 'Support vector':
        return SVC()
    if algorithm == 'K nearest neighbors':
        return KNeighborsClassifier()
    if algorithm == 'Random forest':
        return RandomForestClassifier()

Вече имаме всичко готово, за да покажем точността на нашия модел. За тази цел използваме „кръстосано валидиране“ и разделяме нашите данни на обучение и тестване с помощта на „Kfold“.

folds = KFold(n_splits = 10, shuffle=True, random_state = 0)
cv_score = cross_val_score(pipeline, X, y, cv=folds)
st.subheader('Results')
st.write('Accuracy : ', round(cv_score.mean()*100,2), '%')
st.write('Standard deviation : ', round(cv_score.std()*100,2), '%')

Краен резултат

Сега стигнахме до края на този урок. Трябва да получите приложение, което изглежда така.

Виждаме, че с добри стъпки на предварителна обработка и алгоритъм KNN постигаме точност от 80,25%. Можете да си поиграете с различните опции за предварителна обработка, за да видите как влияят върху резултата.

Заключение

Току-що създадохме интерактивно приложение за прилагане на техники за предварителна обработка и алгоритми за машинно обучение към набор от данни, за да правим прогнози. Видяхме как да използваме тръбопроводи и как да създаваме уиджети за взаимодействие с потребителя на приложението.

Следващи стъпки

Има много неща, които можете да направите, за да подобрите приложението. На върха на ума ми бихте могли:

  • прилагайте кодиране на текст с CountVectorizer
  • добавете повече алгоритми за класификация и оставете потребителя да избере хиперпараметрите
  • използвайте друг набор от данни
  • създайте подобно приложение за набори от регресионни данни

Връзки

  • Моят предишен урок за разбиране на основите на streamlit.
  • Урок за по-добро разбиране на тръбопроводите можете да намерите тук.
  • Точният код на този урок и csv файловете могат да бъдат намерени тук
  • Създавам по-напреднал проект въз основа на този, който може да бъде достъпен на адрес https://ml-exploration-tool.herokuapp.com/
  • Пълният код за този разширен проект е достъпен в моя github.