В настоящее время нейронные сети намного быстрее, чем раньше, благодаря аппаратным улучшениям, и их также легче разрабатывать. Но действительно ли они так нужны?
Далее рассмотрим использование алгоритма глубокого обучения (многослойный перцептрон), который в последствии сравним с простейшим и популярным методом классического машинного обучения - линейной регрессией.
Итак, в данном примере будем применять питон и его известные библиотеки (numpy, pandas, sklearn и т.д.). Импортируем их:
import matplotlib.pyplot as plt import numpy as np import pandas as pd import seaborn as sns from numpy.linalg import inv import time import plotly.graph_objects as go import plotly.express as px from sklearn.neural_network import MLPRegressor from sklearn.model_selection import train_test_split
plotly здесь испольузется для 3D визуализации и необязателен для импортирования.
В этом первом примере генерируем некоторые квадратично коррелированные данные, чтобы показать, что линейную регрессию можно использовать и для моделирования полиномиальных функций.
Создаем датасет:
x_1 = np.linspace(-5,5,1000) x_2 = np.linspace(-5,5,1000) x_2 = np.random.choice(x_2,len(x_2)) X = np.array([x_1,x_2]).T data = pd.DataFrame(X,columns=['x1','x2']) c0 = 2.8 c1 = 3.1 c2 = 1.5 c3 = 0.8 t = c0+c1*x_1+c2*x_2+c3*x_1*x_2 data['t'] = t data.head()
t = c_0 +c_1*x+c_2*y+c_3*x*y
Конечно, эта модель не совсем линейна. Но, если подумать, x и y в основном являются одной и той же переменной, поэтому ее можно рассматривать примерно так:
t = c_0 +(c_1+c_2)*x+c_3*x*x
Нарисуем поверхность:
fig = go.Figure(data=[go.Scatter3d( x=data.x1, y=data.x2, z=data.t, mode='markers', marker=dict( size=4, color=data.t, # set color to an array/list of desired values colorscale='plasma', # choose a colorscale opacity=0.8 ) )]) fig.update_layout(margin=dict(l=0, r=0, b=0, t=0)) fig.show()
Линейная регрессия
Теперь определим прогнозирующую функцию:
def y(W,data): """x needs to be a bidimensional element to make it work""" y=[] for x in data: y_x=0 X=[1,x[0],x[1],x[0]*x[1]] for i in range(len(X)): y_x=y_x+W[i]*X[i] y.append(y_x) return y
Т.к. данные были заранее составлены, их параметры точно известны и можно проверить, работает ли функция предсказания или нет:
C = [c0,c1,c2,c3] exact_pred = y(C,X) exact_result = t exact_data = pd.DataFrame([exact_pred,exact_result]).T exact_data.columns=['Predicted','Target'] exact_data.head()
sns.lineplot(x='Predicted',y='Target',data=exact_data) plt.xlabel('Predicted',fontsize=30) plt.ylabel('Target',fontsize=30) plt.xticks(fontsize=15) plt.yticks(fontsize=15) >> (array([-30., -20., -10., 0., 10., 20., 30., 40., 50.]), [Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, '')])
Функция потерь, используемая в задаче линейной регрессии, представляет собой среднеквадратичную ошибку, которая имеет следующее выражение:
def L(W,X,t): return np.round(0.5*((y(W,X)-t)**2).sum(),10)
И далее определяем градиент функции потерь, который должен стремиться к нулю:
def gradient_L_W(W,X,t): grad_0 = (y(W,X)-t).sum() grad_1 = ((y(W,X)-t)*X[:,0]).sum() grad_2 = ((y(W,X)-t)*X[:,1]).sum() grad_3 = ((y(W,X)-t)*X[:,1]*X[:,0]).sum() return np.round([grad_0,grad_1,grad_2,grad_3],10)
В данном "идеальном примере" функция потерь и градиент предсказуемо равны 0:
print('Loss function for the right parameters is %.2f'%(L(C,X,t))) >>> Loss function for the right parameters is 0.00
print('The gradient of this function for the right parameters is',gradient_L_W(C,X,t)) >>> The gradient of this function for the right parameters is [-0. 0. -0. -0.]
Оптимальное решение задачи линейной регрессии следующее:
Если мы рассмотрим следующую функцию потерь:
Получаем, установив градиент на 0:
def phi(X): phi_0 = np.ones(len(X)) phi_1 = X[:,0] phi_2 = X[:,1] phi_12 = X[:,0]*X[:,1] phi = np.array([phi_0,phi_1,phi_2,phi_12]).T return phi def W_opt(X,t): phi_X = phi(X) A = inv(phi_X.T@phi_X)@phi_X.T w_opt =A@t return w_opt print('The coefficients that we found are the following ones:',W_opt(X,t)) print('The real coefficients are the following ones:',C) >>> The coefficients that we found are the following ones: [2.8 3.1 1.5 0.8] The real coefficients are the following ones: [2.8, 3.1, 1.5, 0.8]
Метод работает отлично. Тут подразумевается, что единственная неопределенность — это числовая ошибка, которую механизм может допустить при вычислении обратной матрицы и матричного умножения, что довольно маловероятно по сравнению с ошибкой алгоритма машинного обучения.
Хоть и был использован весь набор данных, но на самом деле в этом случае действительно нужно только 4 точки данных, и получим тот же точный результат.
Deep Learning (глубокое обучение)
В алгоритме глубокого обучения всё делается по другому.
Есть данные, есть несколько скрытых слоев, которые обрабатывают входные данные, и выходной узел, который дает прогноз. Определяем функцию оценки, которая находится в диапазоне от 0 до 1, и хотим ее максимизировать. В данном случае используем метрику R2:
Делаем разделение на тренировочную и тестовую выборки:
train_number = int(len(data)*0.8) data_shuffle = data.sample(frac=1).reset_index().drop('index',axis=1) train_data = data_shuffle.drop('t',axis=1) X_train = train_data.loc[0:train_number-1] X_test = train_data.loc[train_number::] print('The training set dimension is:',X_train.shape) print('The test set dimension is:', X_test.shape) y_train = data_shuffle['t'].loc[0:train_number-1] y_test = data_shuffle['t'].loc[train_number::] >>> The training set dimension is: (800, 2) >>> The test set dimension is: (200, 2)
Смотрим оценку по метрике:
regr = MLPRegressor(random_state=1, max_iter=3000,
hidden_layer_sizes=(5,3)).fit(X_train, y_train) print('The R2 score of our prediction is %.4f' %(regr.score(X_test, y_test))) >>> The R2 score of our prediction is 0.9915
Визуализируем результат:
test_data = data_shuffle.loc[train_number::] pred_data = test_data.append(test_data).reset_index().drop('index',axis=1) pred_data['Z'] = test_data.t.tolist()+regr.predict(X_test).tolist() pred_data['Target/Prediction'] = ['Target']*len(test_data)+['Prediction']*len(test_data) fig = px.scatter_3d(pred_data, x='x1', y='x2', z='Z', color='Target/Prediction') fig.update_traces(marker_size = 4) fig.show()
Метрика составляет 99,2%. Это хороший результат, но меньший по сравнению с точным результатом, полученным от линейной регрессии.
Таким образом, если имеется линейная или полиномиальная проблема, то ее можно легко решить с помощью модели линейной регрессии. В этом случае не только не нужна нейронная сеть, но и даже можно получить более низкую производительность, если использовать нейронную сеть вместо линейной регрессии.
Второй пример
Усложним предыдущую ситуацию, добавив функцию sin со случайной амплитудой:
x_1 = np.linspace(-5,5,1000) x_2 = np.linspace(-5,5,1000) x_2 = np.random.choice(x_2,len(x_2)) X = np.array([x_1,x_2]).T data = pd.DataFrame(X,columns=['x1','x2']) t = c0+c1*x_1+c2*x_2+c3*x_1*x_2+
np.random.choice(np.linspace(-5,5,10),len(x_1))*np.sin(x_1) data['t']=t fig = go.Figure(data=[go.Scatter3d( x=data.x1, y=data.x2, z=data.t, mode='markers', marker=dict( size=4, color=data.t, # set color to an array/list of desired values colorscale='plasma', # choose a colorscale opacity=0.8 ) )]) fig.update_layout(margin=dict(l=0, r=0, b=0, t=0)) fig.show()
Имеем:
t = c_0 +c_1*x+c_2*y+c_3*x*y + Rsin(x)
где R - это случайная амплитуда от -5 до 5.
Если использовать модель линейной регрессии, получим тот же результат, что и раньше. Это означает, что линейная модель делает все возможное, но видим сильные потери.
pred_w = W_opt(X,t) print('The coefficients that we found are the following ones:',pred_w) print('The real coefficients are the following ones:',C) print('Our loss function value is %.2f'%(L(pred_w,X,t))) >>> The coefficients that we found are the
following ones: [2.95883299 3.09447238 1.53884188 0.79436024] The real coefficients are the following ones: [2.8, 3.1, 1.5, 0.8] Our loss function value is 2664.41
Если использовать глубокое обучение, результат будет очень низким:
train_number = int(len(data)*0.8) data_shuffle = data.sample(frac=1).reset_index().drop('index',axis=1) train_data = data_shuffle.drop('t',axis=1) X_train = train_data.loc[0:train_number-1] X_test = train_data.loc[train_number::] print('The training set dimension is:',X_train.shape) print('The test set dimension is:', X_test.shape) y_train = data_shuffle['t'].loc[0:train_number-1] y_test = data_shuffle['t'].loc[train_number::] >>> The training set dimension is: (800, 2) >>> The test set dimension is: (200, 2)
С другой стороны, нейронная сеть выдает очень низкую ошибку (R2 почти 1).
regr = MLPRegressor(random_state=1, max_iter=3000,
hidden_layer_sizes=(5,3)).fit(X_train, y_train) print('The R2 score of our prediction is %.4f' %(regr.score(X_test, y_test))) >>> The R2 score of our prediction is 0.9920
Опять же, R2=99,20%, что отлично по сравнению с огромными потерями предыдущего метода.
В этом случае, когда модель не является полиномиальной, а нарушена случайной синусоидальной функцией, мы получаем, что линейная модель работает плохо, а модель глубокого обучения работает очень хорошо.
Время вычислений
Имея дело с данными высокой размерности, действительно хотелось бы использовать машинное обучение даже для проблемы регрессии.
Например, инвертируем случайную матрицу разных размеров и построим время, необходимое для этой инверсии::
dimensions = np.arange(1,2000,1) comp_time = [] for d in dimensions: print('Dimensionality = %i'%(d)) start_time = time.time() X = np.random.rand(d,d) np.linalg.inv(X) comp_time.append(time.time() - start_time) plt.figure(figsize=(20,20)) plt.plot(dimensions,comp_time) plt.xlabel('Dimensionality of the matrix',fontsize=20,weight='bold') plt.ylabel('Computational Time',fontsize=20,weight='bold') plt.yticks(fontsize=30) plt.xticks(fontsize=30)
В среде глубокого обучения можно попробовать использовать некоторую сверточную сеть и найти численное решение, которое может быть немного далеким от аналитического, но займет гораздо меньше времени.
Вывод всегда следующий: сначала посмотрим на данные.
Если замечено некоторое «линейное» или «полиномиальное» поведение, то может не стоит использовать глубокое обучение, а просто применить модель линейной регрессии и в итоге получить очень низкую долю ошибки.
Если же поведение странное и его сложно назвать линейным или полиномиальным, возможно, имеет смысл использовать Deep Learning, если имеется достаточно данных.
Если Вам понравилась статья, пожалуйста, поставьте лайк, сделайте репост или оставьте комментарий. Если у Вас есть какие-либо замечания, также пишите комментарии.
Комментариев нет :
Отправить комментарий