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

В наборе данных Grade Point у нас есть 42 функции разных курсов. В соответствии с этим мы должны предсказать CGPA. Мы можем решить эту проблему, используя алгоритмы ОБУЧЕНИЯ С УЧЕНИЕМ С УЧЕНИЕМ.

НАСТРОЙКА⚙️:

ИСПОЛЬЗУЕМЫЕ БИБЛИОТЕКИ:

  • Numpy (библиотека Python, используемая для работы с массивами) 
  • Pandas (пакет Python, который наиболее широко используется для обработки данных/анализа данных и задач машинного обучения) 
  • Matplotlib (обширная библиотека для создания статических, анимированных и интерактивных визуализаций на Python) 
  • Seaborn (библиотека визуализации данных Python на основе matplotlib. Предоставляет высокоуровневый интерфейс для рисования привлекательной и информативной статистической графики) 
  • Sklearn (предоставляет набор эффективных инструментов для машинного обучения и статистического моделирования, включая классификацию, регрессию, кластеризацию и уменьшение размерности через согласованный интерфейс на Python) 
  • Joblib (Joblib — это набор инструментов для облегчения конвейерной обработки в Python). Мы использовали его для хранения весов наших моделей в файлах Joblib.
##Import libraries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn import tree
import joblib
import mglearn

Укажите путь к набору данных,

path='/The_Grades_Dataset.csv'

Загрузите набор данных,

dataset = pd.read_csv(path)

1. Визуализация и предварительная обработка данных📊

1 .1. НАБОР ДАННЫХ:

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

dataset.head()

#Discribtion of CGPA Column only.
dataset.describe()

dataset.columns
#Data type of each column
dataset.dtypes

ИНФОРМАЦИЯ:

  • «CGPA» — целевой столбец/переменная. 
  • «Место №» не влияет на целевую переменную «CGPA». Таким образом, мы можем удалить его из данных. 
  • Поскольку в столбцах «CS-412» и «CS-406» много пропущенных значений, мы можем удалить их из данных. 
  • «PH-121», «HS-101», «CS-105», «CGPA» не имеют пропущенных значений.  В других столбцах меньше пропущенных значений. Мы должны вменить тех, кто использует разные методы

1 .2. ВИЗУАЛИЗАЦИЯ ОТДЕЛЬНЫХ ОСОБЕННОСТЕЙ:

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

plt.figure(figsize=(20,70))
for i in range(1,len(dataset.columns)-2):
    plt.subplot(11,4,i)
    dataset[[dataset.columns[i]]].value_counts().sort_index()
    .plot(kind='bar', title=" ")

1 .3. МЕТОДЫ КОДИРОВАНИЯ:

Поскольку большая часть алгоритма машинного обучения sklearn не может манипулировать категориальным набором данных, поэтому нам необходимо преобразовать категориальные данные в числовые значения с использованием соответствующих методов кодирования. Для этого у нас есть четыре наиболее распространенных метода.

1 .3.1. ФАКТИВНОЕ КОДИРОВАНИЕ. Не подходит для нашего набора данных, так как у нас есть несколько оценок.

1 .3.2. ЧИСЛОВОЕ КОДИРОВАНИЕ: не подходит для нашего набора данных, так как мы можем найти взаимосвязь между оценками.

1 .3.3. ONE-HOT ENCODING: это усложнило бы набор данных, поскольку у нас уже есть 42 столбца.

1 .3.4. ПОРЯДКОВОЕ КОДИРОВАНИЕ: Баллы оценок связаны и упорядочены друг с другом. (т.е. A+ имеет большее значение, чем D-).

Мы присвоили более высокие числовые значения более высоким баллам.

dataset= dataset.replace({'A+':11, 'A':11, 'A-':10, 'B+':9, 'B':8, 'B-':7, 'C+':6, 'C':5, 'C-':4, 'D+':3, 'D':2, 'F':0, 'P':1, 'IP':0, 'X':0, 'I':0, 'W':0, 'WU':0})

Набор данных после кодирования,

1.4. СООТНОШЕНИЯ МЕЖДУ СТОЛБЦАМИ:

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

corr_matrix = dataset.corr()
plt.figure(figsize=(25, 24))
sns.heatmap(data = corr_matrix,cmap='BrBG', annot=True, linewidths=0.2)

ИНФОРМАЦИЯ:

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

1.5. ОБРАБОТКА ОТСУТСТВУЮЩИХ ЗНАЧЕНИЙ:

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

First_year_course=dataset.iloc[:,1:12]
Second_year_course=dataset.iloc[:,12:23]
Third_year_course=dataset[['MT-331','EF-303','HS-304','CS-301','CS-302','TC-383','EL-332','CS-318','CS-306','CS-312','CS-317']]
final_year_course=dataset[['MT-442','CS-403','CS-421','CS-406','CS-414','CS-419','CS-423','CS-412']]

15.1. Присвоить всем столбцам курсов всех лет класс большинства (MODE). 

  • Во-первых, данные данные категоричны. 
  • Во-вторых, набор данных имеет перекос (очень небольшой). 

Вот почему заполнение недостающих данных с помощью режима — лучший вариант.

First_year_course["CY-105"]=First_year_course["CY-105"].fillna(First_year_course["CY-105"].mode()[0])
First_year_course["HS-105/12"]=First_year_course["HS-105/12"].fillna(First_year_course["HS-105/12"].mode()[0])
First_year_course["MT-111"]=First_year_course["MT-111"].fillna(First_year_course["MT-111"].mode()[0])
First_year_course["CS-106"]=First_year_course["CS-106"].fillna(First_year_course["CS-106"].mode()[0])
First_year_course["EL-102"]=First_year_course["EL-102"].fillna(First_year_course["EL-102"].mode()[0])
First_year_course["EE-119"]=First_year_course["EE-119"].fillna(First_year_course["EE-119"].mode()[0])
First_year_course["ME-107"]=First_year_course["ME-107"].fillna(First_year_course["ME-107"].mode()[0])
First_year_course["CS-107"]=First_year_course["CS-107"].fillna(First_year_course["CS-107"].mode()[0])
Second_year_course["HS-205/20"]=Second_year_course["HS-205/20"].fillna(Second_year_course["HS-205/20"].mode()[0])
Second_year_course["MT-222"]=Second_year_course["MT-222"].fillna(Second_year_course["MT-222"].mode()[0])
Second_year_course["EE-222"]=Second_year_course["EE-222"].fillna(Second_year_course["EE-222"].mode()[0])
Second_year_course["MT-224"]=Second_year_course["MT-224"].fillna(Second_year_course["MT-224"].mode()[0])
Second_year_course["CS-210"]=Second_year_course["CS-210"].fillna(Second_year_course["CS-210"].mode()[0])
Second_year_course["CS-211"]=Second_year_course["CS-211"].fillna(Second_year_course["CS-211"].mode()[0])
Second_year_course["CS-203"]=Second_year_course["CS-203"].fillna(Second_year_course["CS-203"].mode()[0])
Second_year_course["CS-214"]=Second_year_course["CS-214"].fillna(Second_year_course["CS-214"].mode()[0])
Second_year_course["CS-203"]=Second_year_course["CS-203"].fillna(Second_year_course["CS-203"].mode()[0])
Second_year_course["CS-214"]=Second_year_course["CS-214"].fillna(Second_year_course["CS-214"].mode()[0])
Second_year_course["EE-217"]=Second_year_course["EE-217"].fillna(Second_year_course["EE-217"].mode()[0])
Second_year_course["CS-212"]=Second_year_course["CS-212"].fillna(Second_year_course["CS-212"].mode()[0])
Second_year_course["CS-215"]=Second_year_course["CS-215"].fillna(Second_year_course["CS-215"].mode()[0])
Third_year_course['MT-331']=Third_year_course['MT-331'].fillna(Third_year_course['MT-331'].mode()[0])
Third_year_course['EF-303']=Third_year_course['EF-303'].fillna(Third_year_course['EF-303'].mode()[0])
Third_year_course['HS-304']=Third_year_course['HS-304'].fillna(Third_year_course['HS-304'].mode()[0])
Third_year_course['TC-383']=Third_year_course['TC-383'].fillna(Third_year_course['TC-383'].mode()[0])
Third_year_course['EL-332']=Third_year_course['EL-332'].fillna(Third_year_course['EL-332'].mode()[0])
Third_year_course['CS-318']=Third_year_course['CS-318'].fillna(Third_year_course['CS-318'].mode()[0])
Third_year_course['CS-306']=Third_year_course['CS-306'].fillna(Third_year_course['CS-306'].mode()[0])
Third_year_course['CS-312']=Third_year_course['CS-312'].fillna(Third_year_course['CS-312'].mode()[0])
Third_year_course['CS-317']=Third_year_course['CS-317'].fillna(Third_year_course['CS-317'].mode()[0])
Third_year_course['CS-301']=Third_year_course['CS-301'].fillna(Third_year_course['CS-301'].mode()[0])
Third_year_course['CS-302']=Third_year_course['CS-302'].fillna(Third_year_course['CS-302'].mode()[0])
final_year_course['MT-442']=final_year_course['MT-442'].fillna(final_year_course['MT-442'].mode()[0])
final_year_course['CS-403']=final_year_course['CS-403'].fillna(final_year_course['CS-403'].mode()[0])
final_year_course['CS-421']=final_year_course['CS-421'].fillna(final_year_course['CS-421'].mode()[0])
final_year_course['CS-414']=final_year_course['CS-414'].fillna(final_year_course['CS-414'].mode()[0])
final_year_course['CS-419']=final_year_course['CS-419'].fillna(final_year_course['CS-419'].mode()[0])
final_year_course['CS-423']=final_year_course['CS-423'].fillna(final_year_course['CS-423'].mode()[0])

Проверить все столбцы, если они остались с нулевым значением?

First_year_course.isnull().sum()
Second_year_course.isnull().sum()
Third_year_course.isnull().sum()
final_year_course.isnull().sum()

1.5.2. Удаление функций:

  • В ОК-406 и ОК-412 85 и 79 учащихся отсутствуют, что очень много. 
final_year_course=final_year_course.drop(['CS-406','CS-412'],axis=1)
  • Удаление этих двух столбцов из набора данных.

1.6. НОРМАЛИЗАЦИЯ И СТАНДАРТИЗАЦИЯ:

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

2. МОДЕЛИ И ПРОГНОЗЫ🔮:

Мы должны разделить наши столбцы на функции и цели. First_year_course, Second_year_course, Third_year_course и final_year_course — это характеристики, а столбец CGPA — цель.

target=dataset['CGPA']
target

Мы разработаем и сравним несколько моделей в соответствии со следующим описанием: 

  • Модель 1: предсказать окончательный CGPA только на основе GP первого года. 
  • Модель 2: предсказать окончательный CGPA на основе GP первых двух лет. 
  • Модель 3: предсказать окончательный CGPA на основе ВОП всех лет

Мы должны предсказать CGPA, который является непрерывным значением, поэтому мы применим АЛГОРИТМЫ РЕГРЕССИИ.

2.1. Модель 1. Предсказание окончательного CGPA только на основе GP первого года.

from  sklearn.tree import DecisionTreeRegressor
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score,r2_score,mean_squared_error
from sklearn import tree
mx1_train,mx1_test,my1_train,my1_test=train_test_split(First_year_course,target,test_size=0.2)

2.1.1. Регрессия дерева решений:

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

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

model_1_a=DecisionTreeRegressor(max_depth=3).fit(mx1_train,my1_train)
prediction_1_a=model_1_a.predict(mx1_test)

давайте визуализируем все узлы.

fig = plt.figure(figsize=(15,10))
_ = tree.plot_tree(model_1_a, feature_names=First_year_course.columns, filled=True)

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

# R2 ADJUSTED SCORE
Adj_r2 = 1 - (1-r2_score(my1_test, prediction_1_a)) * (len(my1_train)-1)/(len(my1_train)-mx1_train.shape[1]-1)
print(round(Adj_r2,4))

Скорректированная оценка R2, которую мы получаем, составляет «0,3680». Это не лучший показатель для нашей модели.

Попробуем другой алгоритм.

2.1.2. Линейная регрессия

Линейная регрессия — это алгоритм машинного обучения, основанный на обучении с учителем. Он выполняет задачу регрессии. Регрессия моделирует целевое значение прогноза на основе независимых переменных. Он в основном используется для выяснения отношений между переменными.

model_1_b=LinearRegression().fit(mx1_train,my1_train)
prediction_1_b=model_1_b.predict(mx1_test)

построить линейную кривую.

fig, axes = plt.subplots(4, 3,figsize=(20,20))
j=0
k=0
for i in First_year_course.columns:
     if j==4:
        break
     sns.regplot(x=i, y=target,    data=First_year_course,ax=axes[j,k]).set(title=f'Regression plot of {i} and CGPA')
     k+=1
     if k==3:
        j+=1
        k=0

# The coefficients
print("1. Coefficients: \n", model_1_b.coef_)
# The coefficients
print("2. Coefficients: ", model_1_b.intercept_)
# The mean squared error
print("3. Mean squared error: %.2f" % mean_squared_error(my1_test, prediction_1_b))
# The coefficient of determination: 1 is perfect prediction
print("4. R Square Score: %.2f" % r2_score(my1_test, prediction_1_b))
# R adjusted Squre
Adj_r2 = 1 - (1-r2_score(my1_test, prediction_1_b)) * (len(my1_train)-1)/(len(my1_train)-mx1_train.shape[1]-1)
print("5. R Adjusted Square Score %.2f" % Adj_r2 )

Производительность линейной регрессии лучше, чем дерево решений для нашей модели.

2.2. Модель 2: предсказать окончательный CGPA на основе GP первых двух лет.

2.2.1. Регрессор K ближайших соседей:

Регрессия KNN - это непараметрический метод, который интуитивно аппроксимирует связь между независимыми переменными и непрерывным результатом путем усреднения наблюдений в той же окрестности.

model_2_a=KNeighborsRegressor().fit(mx2_train,my2_train)
prediction_2_a=model_2_a.predict(mx2_test)

позволяет визуализировать модель,

plt.plot(my2_test, model_2_a.predict(mx2_test))
plt.plot(mx2_train,my2_train, '^', c=mglearn.cm2(0),   markersize=8)
plt.plot(mx2_test,my2_test, 'v', c=mglearn.cm2(1), markersize=8)
plt.title("{} neighbor(s)\n train score: {:.2f} test  score: {:.2f}".format(5,    model_2_a.score(mx2_train, my2_train),model_2_a.score(mx2_test, my2_test)))
plt.xlabel("Feature")
plt.ylabel("Target")
plt.legend(["Model predictions", "Training data/target","Test   data/target"], loc="best")

print(r2_score(my2_test,prediction_2_a))

оценка r2 = 0,7982

2.2.2. Регрессор случайного леса:

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

model_2_b=RandomForestRegressor().fit(mx2_train,my2_train)
prediction_2_b=model_2_b.predict(mx2_test)
estimator = model_2_b.estimators_[5]
from sklearn.tree import export_graphviz
# Export as dot file
export_graphviz(estimator,
                out_file='tree.dot',
                feature_names = combined_2.columns,
                class_names = target,
                rounded = True, proportion = False,
                precision = 2, filled = True)
# Convert to png
from subprocess import call
call(['dot', '-Tpng', 'tree.dot', '-o', 'tree.png', '-Gdpi=600'])
# Display in jupyter notebook
from IPython.display import Image
Image(filename = 'tree.png')

2.3. Модель 3: спрогнозируйте окончательный CGPA на основе GP всех лет.

combined_3=pd.concat([First_year_course,Second_year_course,Third_year_course,final_year_course],axis=1)
mx3_train,mx3_test,my3_train,my3_test=train_test_split(combined_3,target,test_size=0.2)

импортировать библиотеки

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.layers import  Dense
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import GradientBoostingRegressor

а. Нейронная сеть прямой связи с использованием TensorFlow:

Нейронные сети с прямой связью также известны как многослойная сеть нейронов (MLN). Эти сети моделей называются упреждающими, потому что информация проходит только вперед в нейронной сети, через входные узлы, затем через скрытые слои (один или несколько слоев) и, наконец, через выходные узлы.

model_3_a = keras.Sequential([
            Dense(20, activation=tf.nn.swish, input_shape=           
            [len(combined_3.columns)]),
            Dense(1)])

Слои:

tf.keras.utils.plot_model(model_3_a, to_file="my_model.png", show_shapes=True)

model_3_a.summary()

model_3_a.compile(optimizer='adam',loss='mse',metrics=['mae', 'mse'])
early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=50)

history = model_3_a.fit(mx3_train, my3_train, epochs=150, verbose=0, validation_split = 0.1,callbacks=[early_stop])
hist = pd.DataFrame(history.history)
hist['epoch'] = history.epoch
rmse_final = np.sqrt(float(hist['val_mse'].tail(1)))
print()
print('Final Root Mean Square Error on validation set: {}'.format(round(rmse_final, 3)))

Функция активации: сначала мы используем RELU и линейную функцию активации, но ее производительность с наши данные нас не удовлетворяют. Переключаемся на функцию активации SWISH. Мы наблюдаем меньше ошибок при использовании swish.

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

Оптимизатор: Adam — это алгоритм оптимизации, заменяющий стохастический градиентный спуск для обучения моделей глубокого обучения. Адам сочетает в себе лучшие свойства алгоритмов AdaGrad и RMSProp, чтобы предоставить алгоритм оптимизации, который может обрабатывать разреженные градиенты в задачах с шумом.

История:с увеличением эпохи мы можем наблюдать, что потери уменьшаются.

print(hist)

Следующий график показывает;

  • Функция потерь также уменьшается с увеличением эпохи.
  • Модель отлично работает с проверочным набором.
def plot_history():
   plt.figure()
   plt.xlabel('Epoch')
   plt.ylabel('Mean Square Error [Thousand Dollars$^2$]')          
   plt.plot(hist['epoch'], hist['mse'], label='Train Error')
   plt.plot(hist['epoch'], hist['val_mse'], label = 'Val Error')
   plt.legend()
   plt.ylim([0,50])
plot_history()

mse, _, _ = model_3_a.evaluate(mx3_test, my3_test)
rmse = np.sqrt(mse)
print('Root Mean Square Error on test set: {}'.format(round(rmse, 3)))

prediction_3_a=model_3_a.predict(mx3_test)
r2_score(prediction_3_a,my3_test)

а. Регрессор повышения градиента

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

model_3_b = GradientBoostingRegressor().fit(mx3_train,my3_train)
predictions_3_b = model_3_b.predict(mx3_test)
print(r2_score(predictions_3_b,my3_test))

Для построения графика давайте сначала получим все параметры.

paras=model_3_b.get_params()
sc = StandardScaler()
mx3_test_std = sc.fit_transform(mx3_test)
test_score = np.zeros((paras['n_estimators'],), dtype=np.float64)
for i, y_pred in enumerate(model_3_b.staged_predict(mx3_test_std)):
test_score[i] = model_3_b.loss_(my3_test, predictions_3_b)
fig = plt.figure(figsize=(8, 8))
plt.subplot(1, 1, 1)
plt.title('Deviance')
plt.plot(np.arange(paras['n_estimators']) + 1, model_3_b.train_score_, 'b-',
label='Training Set Deviance')
plt.plot(np.arange(paras['n_estimators']) + 1, test_score, 'r-',label='Test Set Deviance')
plt.legend(loc='upper right')
plt.xlabel('Boosting Iterations')
plt.ylabel('Deviance')
fig.tight_layout()
plt.show()

Следующий график показывает;

  • Отклонение также уменьшается с увеличением числа итераций.
  • Модель хорошо работает и с тестовым набором.

Фу! Это все для этого урока. Код развертывания вы найдете на github.