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

Сегодня мы собираемся создать с нуля множественную линейную регрессию, используя Python, и сравнить нашу модель с моделью sklearn.
Кроме того, давайте построим наши прогнозы в 3D. Это очень весело, так что давайте начнем…

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

Зависимая переменная = точка пересечения Y + наклон * независимая переменная

Множественная линейная регрессия

Зависимая переменная = Y - точка пересечения + наклон(i) * независимая переменная(i)
Или
y_hat = смещение + W(1).x(1) + W(2).x( 2) + …. + W(n).x(n)

Шаг 1: Создайте несколько фиктивных данных

from sklearn.datasets import make_regression
X, y, coef = make_regression(n_samples=1000,
                             n_features=2,
                             n_informative=2,
                             noise=10.0,
                             bias=1.0,
                             coef=True,
                             random_state=42)

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

# yhat = '(w1*x1)+(w2*x2)+bias'
df = pd.DataFrame(
           data={'feature1':X[:,0],
                 'feature2':X[:,1],
                 'target (y)':y, 
                 'weight1':coef[0],
                 'weight2':coef[1], 
                 'bias':1 , 
                 'y_hat': ((coef[0]*X[:,0])+(coef[1]*X[:,1]))+1 })
df.head() # let view the dataframe

Шаг 2: Визуализируйте данные

plt.figure(figsize=(12,8))
plt.subplot(2,2,1)
sns.regplot(x='feature1',y='target (y)', data=df , marker='x', color='lightblue')
plt.subplot(2,2,2)
sns.regplot(x='feature2',y='target (y)', data=df , marker='x', color='lightblue')
plt.subplot(2,2,3)
sns.regplot(x='feature1',y='y_hat', data=df , marker='+', color='salmon')
plt.subplot(2,2,4)
sns.regplot(x='feature2',y='y_hat', data=df , marker='+', color='salmon')


plt.show()

We can use sns.jointplot() Draw a plot of two variables with bivariate and univariate graphs.
sns.jointplot(x='feature'
              y='target',
              data=df ,
              marker='x')
TO Plot pairwise relationships in a dataset 
use sns.pairplot()
sns.pairplot(dataframe);

Шаг 3: Данные обучения и тестирования

Этот шаг очень важен для предотвращения утечки данных.

X = df[[‘feature1’,’feature2']]
y = df[‘target (y)’]
print(‘Shape of Independent variable’,X.shape)
print(‘Shape of Dependent variable’,y.shape)
X_train, X_test, y_train, y_test = train_test_split(X, y)
print(‘Shape of Training data X’,X_train.shape)
print(‘Shape of Training data y’,y_train.shape)
print(‘Shape of Testing data X’,X_test.shape)
print(‘Shape of Testing data y’,y_test.shape)

Шаг 4: Весовая матрица и погрешность

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

def get_weight_and_bias(num_w, num_b):
  Weights = np.random.rand(num_w)
  Bias = 0.01*np.random.rand(num_b)
  return Weights,Bias

Шаг 5: Множественная линейная регрессия с нуля

Звучит пугающе, я знаю, но мы можем сделать это всего за несколько строк кода.

def multiple_regression(features, weights, bias):
  y_hat = (features@weights)+bias
  return y_hat

Шаг 6: Функция потерь

Давайте используем среднеквадратичную ошибку в качестве функции потерь
Среднеквадратическая ошибка = среднее значение (y_true — y_preds)**2

def loss_fn(ground_truth, predictions):
  return np.mean(np.square((ground_truth-predictions)))

# for geeks 
# (1/len(ground_truth))*np.sum((ground_truth - predictions)**2)

Шаг 7: Градиентный спуск

Еще один страшный термин, еще несколько строк кода. вот как мы собираемся подойти к этому.

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

Ой.. мальчик еще один термин, потеря поверхности, что это?

Мы прогнозируем зависимую переменную (y), используя независимые переменные X(i)

y_hat = смещение + W(1).x(1) + W(2).x(2) + …. + W(n).x(n)
W(i) = веса , полученные моделью
X(i) = характеристики

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

Как мы можем минимизировать эту ошибку: Изучая правильные веса

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

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

Градиентный спуск

Градиентный спуск — это алгоритм итерационной оптимизации первого порядка для нахождения локального минимума дифференцируемой функции.

Мы используем среднеквадратичную ошибку в качестве функции потерь
loss = 1/N * (y_true — y_pred)**2

Где y_pred = W1*X1 + W2*X2 + …. + Wn*Xn (Множественная регрессия)

error = y_true — ( W1*X1 + W2*X2 + …. + Wn*Xn )

потери производной по весам
-2/N (ошибка)Xi

производная по смещению
-2/N (ошибка)1

Обновление весов и смещения

Мы хотим двигаться в отрицательном направлении градиента

W(k+1)= W(k) + (Learning_rate * -Градиент)

B (k + 1) = B (k) + (Learning_rate * -Градиент)

Достаточно теории, давайте начнем кодировать, давайте создадим этот оптимизатор градиентного спуска.

# I told you, this only sounds scary ... few lines of code that's it
def gradient_descent(features, ground_truth, predictions):
  # difference btw true-values and predicted-values is the error
  error = ground_truth-predictions
  
  # derivative of loss wrt weights
  dW=[]
  for col in features.columns:
    dW.append(-2*np.mean((error*features[col])))

  # derivative of loss wrt bias
  db = -2*np.mean(error)
  
  return dW,db

Обучение модели множественной регрессии

def model_training(epochs,W,b,learning_rate=1e-3):

  for i in range(epochs):
    # get predictions
    y_hat = multiple_regression(X_train, weights=W, bias=b)
    # compute loss
    loss = loss_fn(y_train, y_hat)

    # optimize the model parameters 
    [dW1, dw2], db = gradient_descent(X_train, y_train, y_hat)
    # update the weights
    W[0] = W[0] + (learning_rate*-dW1)
    W[1] = W[1] + (learning_rate*-dW1)
    b = b + (learning_rate*-db)
    
    # print the loss

    print('epoch:',i,' loss:',loss)

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

# Training
model_training(10,W,b)

Полный код множественной линейной регрессии

class Multiple_reg:
  def __init__(self, learning_rate=1e-3, num_weights=2 , 
               bias=0.001*np.random.rand()):
    self.learning_rate = learning_rate
    self.weight = np.random.rand(num_weights)
    self.bias = bias
  
  def multiple_regression(self, features):
    return (features@self.weight)+self.bias

  def loss_fn(self, ground_truth, predictions):
    return np.mean(np.square((ground_truth-predictions)))

  def gradient_descent(self, features, ground_truth, predictions):
    error = ground_truth - predictions
    dW =[]
    for col in features.columns:
      dW.append(-2*np.mean((error*features[col])))
    db = -2*np.mean(error)
    return dW,db
  
  def optimize_model_parametes(self, features, ground_truth, 
                                  predictions):
    dW, db = self.gradient_descent(features, ground_truth, 
                                    predictions)
    self.weight[0] += self.learning_rate * -dW[0]
    self.weight[1] += self.learning_rate * -dW[1]
    self.bias += self.learning_rate * -db

  def fit(self, X, y_true, epochs=10, to_print=False):    
    history={'epoch':[],'loss':[]}
    for epoch in range(epochs):
      y_hat = self.multiple_regression(X)
      loss = self.loss_fn(y_true, y_hat)
      self.optimize_model_parametes(X, y_true, y_hat)
      if to_print:
        print('epoch:',epoch,'loss:',loss)
      history['epoch'].append(epoch)
      history['loss'].append(loss)
    return history
    
  def predict(self ,test_features):
    y_hat = self.multiple_regression(test_features)
    return y_hat
      
  def get_model_coef(self):
    return self.weight, self.bias

  def evaluate(self,test_features,y_test):
    y_hat = self.predict(test_features)
    loss = self.loss_fn(y_test,y_hat)
    return loss

давайте создадим экземпляр модели множественной регрессии, которую мы создали

  • сделать некоторые прогнозы
  • Сравните производительность с моделью sklearn
  • построить прогнозы
model1 = Multiple_reg()
history1 = model1.fit(X_train, y_train, epochs=1000)

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

# Helper functions
# LEARNING CURVE
def plot_learning_curve(model,history):
  model_coef,bias = model.get_model_coef()
  plt.figure(figsize=(8,5))
  plt.plot(history['loss']);
  plt.title(f'Learning curve # Learned Weights:{model_coef} and bias:{bias :.2f}')
  plt.xlabel('Epochs')
  plt.ylabel('Mean squared error')
  plt.show()

# Lets use sklearn Linear model and compare performance
def print_model_coef(model):
  model_coef,bias = model.get_model_coef()
  print(f'Actual Weight:{coef} and bias:{df["bias"][0]}')
  print(f'Learned Weight:{model_coef} and bias:{bias :.2f}')

  from sklearn.linear_model import LinearRegression
  sklearn_model = LinearRegression()
  sklearn_model.fit(X_train, y_train)
  print('Sklearn LinearModel coef Weight:',
         sklearn_model.coef_,
        'bias:',
         sklearn_model.intercept_)

Ух ты..! наша простая модель хорошо справилась с изучением параметров из данных

Давайте сделаем некоторые прогнозы

# make predictions
predictions = model1.predict(X_test)
print('MAE', mean_absolute_error(y_test,predictions))
print('MSE', mean_squared_error(y_test,predictions))

Составление прогнозов

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

Для трехмерного построения нам нужны оси X, оси Y и оси Z.
Мы можем использовать np.meshgrid и нашу модель, чтобы получить требуемую ось.
Кодировать очень просто, а не объяснять длинными словами, так что давайте сразу перейдем к коду.

# utility functions for plotting predictions
def get_axis(x_axis,y_axis,model):
  # datapoints
  X = np.linspace(x_axis.min(), x_axis.max())
  Y = np.linspace(y_axis.min(), y_axis.max())
  # create a mesh grid
  XX, YY = np.meshgrid(X, Y)
  # z-axis
  z = model.predict(pd.DataFrame(
                      {'f1':np.ravel(XX),
                       'f2':np.ravel(YY)}))
  ZZ = np.reshape(z.values, XX.shape)
  return XX,YY,ZZ

Давайте создадим ось X, ось Y и ось Z, используя указанную выше утилиту.

X, Y, Z = get_axis(X_test['feature1'],X_test['feature2'], model1)

Наши 3D-графики

  • Поверхностный участок
  • Контур3D
  • каркас
fig = plt.figure(figsize=(20,8))
# SURFACE PLOT
ax = plt.subplot(1,3,1,projection='3d')
ax.scatter3D(X_test['feature1'], X_test['feature2'], predictions, marker='+', color='red' )
ax.plot_surface(X, Y, Z, cmap='viridis')
ax.set_title('surface plot');

# CONTOUR3D
ax = plt.subplot(1,3,2,projection='3d')
ax.scatter3D(X_test['feature1'], X_test['feature2'], predictions, marker='+', color='red' )
ax.contour3D(X, Y, Z, 50, cmap='binary');
ax.set_title('contour 3D plot');


# WIREFRAME
ax = plt.subplot(1,3,3,projection='3d')
ax.scatter3D(X_test['feature1'], X_test['feature2'], predictions, marker='+', color='red' )
ax.plot_wireframe(X, Y, Z, rcount=10, ccount=10, color='black')
ax.set_title('wireframe');

Ресурсы