В машинном обучении множественная регрессия — это линейный подход к моделированию взаимосвязи между скалярной целевой переменной и одной или несколькими переменными независимых признаков. Случай одной объясняющей переменной называется простой линейной регрессией; для более чем одного процесс называется множественной линейной регрессией.
Сегодня мы собираемся создать с нуля множественную линейную регрессию, используя 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');