线性回归
X和Y之间的关系,数据X就是特征,目标Y就是标签,X的系数就是参数,参数决定X对Y的影响程度(重要程度):
回归就是确定一个维度(一条线、平面)来最好的拟合数据点,使得X对Y的解释力度最好,误差最小,R-squared最大。
假设拟合的平面(决策方程)是:
微调是,使得结果更精准;核心影响是
数据处理时添加一列向量,目的将偏置项放入矩阵计算。
求解出参数后利用真实的特征求解得到的是预测值,而实际生产环境中的真实值
和预测值(即真实值在平面上的投影点)之间肯定存在差异,即线性回归解释的变量(现实中存在的样本)是存在线性关系的。然而这种关系并不是严格的函数映射关系,但是构建的模型(方程)却是严格的函数映射关系的,因此对于每个样本来说,我们拟合的结果会与真实值之间存在一定的误差,该差异定义成误差项
,于是真实值和特征之间的关系表达:
对于误差项越小越好接近于0,定义一个函数Loss函数区拟合数据,使得模型的误差项或Loss最小。
误差项:一般要求iid且
~Gauss(0,
),即
代入后得到
确定x和α的组合所得到的预测值和真实值y之间误差最小,即成为y的可能性越大越好。
iid:输入空间X中的所有样本服从一个隐含未知的分布,训练数据的所有样本都是独立地从这个分布上采样而得。机器学习是利用当前获取到的信息(或数据)进行训练学习,用以对未来的数据进行预测、模拟。所以都是建立在历史数据之上,采用模型去拟合未来的数据。因此需要使用的历史数据具有总体的代表性。要从已有的数据(经验) 中总结出规律来对未知数据做决策,如果获取训练数据是不具有总体代表性的,就是特例的情况,那规律就会总结得不好或是错误,因为这些规律是由个例推算的,不具有推广的效果。因此通过独立同分布的假设,就可以大大减小训练样本中个例的情形。
在自然界与生产中,一些现象受到许多相互独立的随机因素的影响,如果每个因素所产生的影响都很微小时,总的影响可以看作是服从正态分布的,Gauss分布说明了误差影响(浮动)不会很大,极小情况下影响会比较大,但这也属于正常情况,中心极限定理证明在适当的条件下,大量相互独立随机变量的均值经适当标准化后依分布收敛于正态分布。
引入概率论中最大似然函数:什么样的参数跟数据组合后恰好是真实值,由于最大似然函数之间是相乘(iid时联合概率密度等于边缘概率密度乘积),所以引入对数似然函数:
目标函数为(预测值和真实值之间误差越小越好,J(α)即上式中非常数部分):
上式对α求偏导得到(矩阵与矩阵本身转置相乘为对称阵):
令其为0得到
import numpy as np
import os
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
plt.rcParams['axes.labelsize'] = 14
plt.rcParams['xtick.labelsize'] = 12
plt.rcParams['ytick.labelsize'] = 12
import warnings
warnings.filterwarnings('ignore')
np.random.seed(42)
# 随机生成序列
X = 2*np.random.rand(100,1)
y = 4+ 3*X +np.random.randn(100,1)
plt.plot(X,y,'b.')
plt.xlabel('X_1')
plt.ylabel('y')
plt.axis([0,2,0,15])
plt.show()
X_b = np.c_[np.ones((100,1)),X]
theta_best = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y) # array([[4.21509616], [2.77011339]])
X_new = np.array([[0],[2]]) # 随机生成点
X_new_b = np.c_[np.ones((2,1)),X_new]
y_predict = X_new_b.dot(theta_best)
y_predict # array([[4.21509616], [9.75532293]])
plt.plot(X_new,y_predict,'r--')
plt.plot(X,y,'b.')
plt.axis([0,2,0,15])
plt.show()
# 等价线性回归拟合的参数
from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()
lin_reg.fit(X,y)
print (lin_reg.coef_) # θ
print (lin_reg.intercept_) # β
机器学习中核心的思想是迭代更新,评估方法一般有
1、评估项R-squared(决定系数,越接近于1表明模型拟合效果越好):
衡量模型对数据方差的解释能力。值的范围通常是 0 到 1,越接近 1 表示模型拟合越好。值为负数时,表明模型的预测效果比简单的均值模型还差。
2、MAE(Mean Absolute Error,平均绝对误差)
MAE 是实际值与预测值的绝对误差的平均值。它不会对大的误差产生过大的惩罚,适合对每个误差的“公平”处理。
3、MSE(Mean Squared Error,均方误差)
MSE 是实际值与预测值的平方误差的平均值。由于平方的作用,较大的误差会被放大,因此对离群值更加敏感。
4、RMSE(Root Mean Squared Error,均方根误差)
RMSE 是 MSE 的平方根,具有与原始数据相同的单位,通常用于对误差的直观解释。它对大误差特别敏感。
5、MAPE(Mean Absolute Percentage Error,平均绝对百分比误差)
MAPE 衡量的是预测误差相对于实际值的百分比。适用于预测任务,特别是对误差比例敏感的情况。但当实际值接近零时,MAPE 会变得不稳定。
6、RMSLE(Root Mean Squared Logarithmic Error,均方根对数误差)
RMSLE 用于预测时,特别是对目标变量值有较大差异(如指数增长)时。它通过对数变换使得模型对大值和小值的误差更加均衡。
7、Adjusted R²(调整后的 R²)
其中,n 是样本量,p 是特征数量。调整后的 R² 通过惩罚模型中过多的特征,避免模型对过多特征的过拟合,适用于多变量回归分析。
8、Huber Loss
Huber Loss 是 MSE 和 MAE 的结合。对于小误差,它类似 MSE;对于大误差,它转化为 MAE,从而减少离群值的影响。
9、Explained Variance Score
解释方差得分衡量模型的预测结果与实际结果之间方差的比例,值越接近 1,模型越能解释数据的变异性。
10、Confusion Matrix (适用于分类任务)
用于分类问题,是一个用于评估分类模型性能的工具,尤其适用于二分类问题。它呈现了模型预测结果与实际标签之间的关系。混淆矩阵显示了四个基本值:
- True Positive (TP):实际为正类且预测为正类的样本数。
- True Negative (TN):实际为负类且预测为负类的样本数。
- False Positive (FP):实际为负类但预测为正类的样本数(假阳性)。
- False Negative (FN):实际为正类但预测为负类的样本数(假阴性)。
11、Precision、Recall、F1-Score(适用于分类任务)
Accuracy(准确率):正确预测为正例和正确预测为负例站总体的比例。
Precision(精确率):正确预测为正例的样本占预测为正例的比例,衡量模型预测为正类的样本中,实际为正类的比例。它反映了模型预测正类的准确性。精确率高意味着模型在预测为正类时,错误预测的概率较小,假阳性较少。
Recall(召回率):正确预测为正例的样本占实际正例的比例,衡量实际为正类的样本中,被模型正确预测为正类的比例。它反映了模型捕捉到正类样本的能力。召回率高意味着模型能够较好地捕捉到所有的正类样本,假阴性较少。但如果召回率非常高,可能会牺牲精确率,导致假阳性增多。
F1-Score:是精确率和召回率的调和平均值,是两者的平衡指标。当精确率和召回率的权重相似时,F1-Score 是非常有用的评估标准。它可以综合考虑模型的精度和召回能力。F1-Score 越高,表明模型在精确率和召回率之间达到了较好的平衡。它通常用于处理类不平衡问题,因为它在考虑两者的同时,能够避免单一指标过高或过低的问题。
-
Precision 和 Recall 是相互竞争的指标。通常,当精确率提高时,召回率会下降,反之亦然。选择精确率还是召回率取决于任务的具体要求:
- 如果假阳性(误将负类预测为正类)代价较高,可以优先考虑 精确率。
- 如果假阴性(误将正类预测为负类)代价较高,可以优先考虑 召回率。
sklearn中有直接计算的相关方法:
from sklearn.metrics import precision_score,recall_score,f1_score
precision_score(y_train_5,y_train_pred) # 0.7687135020350381
recall_score(y_train_5,y_train_pred) # 0.801328168234643
f1_score(y_train_5,y_train_pred) # 0.7846820809248555
在分类问题中,模型(如逻辑回归、SVM等)通常会输出一个决策分数(Decision Score)或概率值(Probability),表示某个样本属于正类的置信度,阈值会影响P和R的结果大小:
阈值的作用:
-
决定分类边界
-
默认情况下,Scikit-Learn 的
predict()
方法使用 阈值=0.5(对于概率)或 阈值=0(对于 SVM 等模型的决策分数)。 -
如果
decision_function()
或predict_proba()
的输出 ≥ 阈值,则预测为正类(1),否则预测为负类(0)。
-
-
调整模型的表现
-
提高阈值(如从 0.5 → 0.8):
-
模型会更保守,仅对高置信度的样本预测为正类。
-
减少假正例(FP),提高精确率(Precision),但可能降低召回率(Recall)。
-
-
降低阈值(如从 0.5 → 0.2):
-
模型会更激进,将更多样本预测为正类。
-
减少假负例(FN),提高召回率(Recall),但可能降低精确率(Precision)。
-
-
-
应对类别不平衡
-
如果正类样本很少(如欺诈检测),可以降低阈值,避免漏掉正类。
-
如果误报成本很高(如垃圾邮件分类),可以提高阈值,减少误判。
-
同样在分类器的函数中:
y_scores = sgd_clf.decision_function([X[35000]])
y_scores
# 设置阈值
t = 50000
y_pred = (y_scores > t)
y_pred
y_scores = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3,
method="decision_function")
y_scores[:10] # 打印出决策分数
from sklearn.metrics import precision_recall_curve
precisions, recalls, thresholds = precision_recall_curve(y_train_5, y_scores)
y_train_5.shape
thresholds.shape # 阈值的可能情况 (59698,)
precisions[:10] #
precisions.shape # (59699,)
recalls.shape # (59699,)
PR和阈值的关系:
def plot_precision_recall_vs_threshold(precisions,recalls,thresholds):
plt.plot(thresholds,
precisions[:-1],
"b--",
label="Precision")
plt.plot(thresholds,
recalls[:-1],
"g-",
label="Recall")
plt.xlabel("Threshold",fontsize=16)
plt.legend(loc="upper left",fontsize=16)
plt.ylim([0,1])
plt.figure(figsize=(8, 4))
plot_precision_recall_vs_threshold(precisions,recalls,thresholds)
plt.xlim([-700000, 700000])
plt.show()
def plot_precision_vs_recall(precisions, recalls):
plt.plot(recalls,
precisions,
"b-",
linewidth=2)
plt.xlabel("Recall", fontsize=16)
plt.ylabel("Precision", fontsize=16)
plt.axis([0, 1, 0, 1])
plt.figure(figsize=(8, 6))
plot_precision_vs_recall(precisions, recalls)
plt.show()
12、ROC Curve (Receiver Operating Characteristic Curve)
ROC 曲线是评估二分类模型性能的常用工具,特别是在处理类不平衡问题时。ROC曲线不是绘制精确度与召回率,而是绘制了不同的 True Positive Rate (TPR)(召回率)和 False Positive Rate (FPR)(假阳性率)之间的关系。它有助于可视化模型在不同阈值下的表现。
- 横轴为 FPR(假阳性率),纵轴为 TPR(召回率)。
- ROC 曲线展示了在不同阈值下,模型如何平衡假阳性和真阳性的比率。曲线下的面积(AUC)通常用来衡量分类器的性能,AUC越接近 1,模型越好。
- 完美模型的 ROC 曲线会接近左上角(即 FPR=0, TPR=1)。
- 随机猜测的模型的 ROC 曲线接近对角线(即 FPR = TPR)。
from sklearn.metrics import roc_curve
fpr, tpr, thresholds = roc_curve(y_train_5, y_scores) # 标签 得分
def plot_roc_curve(fpr, tpr, label=None):
plt.plot(fpr, tpr, linewidth=2, label=label)
plt.plot([0, 1], [0, 1], 'k--')
plt.axis([0, 1, 0, 1])
plt.xlabel('False Positive Rate', fontsize=16)
plt.ylabel('True Positive Rate', fontsize=16)
plt.figure(figsize=(8, 6))
plot_roc_curve(fpr, tpr)
plt.show()
from sklearn.metrics import roc_auc_score
roc_auc_score(y_train_5, y_scores) # 0.9624496555967156
得到一个目标函数后,如何优化求解目标
梯度下降(优化算法常用)eg:
寻找目标函数(损失函数)的终点(极值点),什么样的参数和数据的组合以什么样的方向使得目标函数最快达到极值点,因为是寻找极小值点,要求Loss最小,所以是梯度的反方向,其次参数更新速度要求要慢,需要沿着梯度的方向寻找极小值点。
批量梯度下降,上式求偏导得到:
当前参数为随机值θj,所以下一次更新的参数为:
容易得到最优解,但是每次考虑所有样本速度慢(这里包含两个参数,偏导后为2x)
eta = 0.1
n_iterations = 1000
m = 100
theta = np.random.randn(2,1)
for iteration in range(n_iterations):
gradients = 2/m* X_b.T.dot(X_b.dot(theta)-y)
theta = theta - eta*gradients
theta # array([[4.21509616], [2.77011339]])
X_new_b.dot(theta) # array([[4.21509616], [9.75532293]]) # y_pedict
学习率对梯度下降结果的影响:
theta_path_bgd = [] # 全样本
def plot_gradient_descent(theta,eta,theta_path = None):
m = len(X_b)
plt.plot(X,y,'b.')
n_iterations = 1000
for iteration in range(n_iterations):
y_predict = X_new_b.dot(theta)
plt.plot(X_new,y_predict,'b-')
gradients = 2/m* X_b.T.dot(X_b.dot(theta)-y)
theta = theta - eta*gradients
if theta_path is not None:
theta_path.append(theta)
plt.xlabel('X_1')
plt.axis([0,2,0,15])
plt.title('eta = {}'.format(eta))
theta = np.random.randn(2,1)
plt.figure(figsize=(10,4))
plt.subplot(131)
plot_gradient_descent(theta,eta = 0.02) # 次数多,精确
plt.subplot(132)
plot_gradient_descent(theta,eta = 0.1,theta_path=theta_path_bgd)
plt.subplot(133)
plot_gradient_descent(theta,eta = 0.5)
plt.show()
随机梯度下降,每次寻找一个样本,迭代速度快但是不一定朝着收敛方向:
theta_path_sgd=[]
m = len(X_b)
np.random.seed(42)
n_epochs = 50
t0 = 5
t1 = 50
def learning_schedule(t):
return t0/(t1+t)
theta = np.random.randn(2,1)
for epoch in range(n_epochs):
for i in range(m):
if epoch < 10 and i<10:
y_predict = X_new_b.dot(theta)
plt.plot(X_new,y_predict,'r-')
random_index = np.random.randint(m)
xi = X_b[random_index:random_index+1]
yi = y[random_index:random_index+1]
gradients = 2* xi.T.dot(xi.dot(theta)-yi)
eta = learning_schedule(epoch*m+i)
theta = theta-eta*gradients
theta_path_sgd.append(theta)
plt.plot(X,y,'b.')
plt.axis([0,2,0,15])
plt.show()
小批量梯度下降法,每次选择更新一小部分数据:
theta_path_mgd=[]
n_epochs = 50
minibatch = 16
theta = np.random.randn(2,1)
t0, t1 = 200, 1000
def learning_schedule(t):
return t0 / (t + t1)
np.random.seed(42)
t = 0
for epoch in range(n_epochs):
shuffled_indices = np.random.permutation(m)
X_b_shuffled = X_b[shuffled_indices]
y_shuffled = y[shuffled_indices]
for i in range(0,m,minibatch):
t+=1
xi = X_b_shuffled[i:i+minibatch]
yi = y_shuffled[i:i+minibatch]
gradients = 2/minibatch* xi.T.dot(xi.dot(theta)-yi)
eta = learning_schedule(t)
theta = theta-eta*gradients
theta_path_mgd.append(theta)
theta # array([[4.25490684], [2.80388785]])
对比:
theta_path_bgd = np.array(theta_path_bgd)
theta_path_sgd = np.array(theta_path_sgd)
theta_path_mgd = np.array(theta_path_mgd)
plt.figure(figsize=(12,6))
plt.plot(theta_path_sgd[:,0],theta_path_sgd[:,1],'r-s',linewidth=1,label='SGD')
plt.plot(theta_path_mgd[:,0],theta_path_mgd[:,1],'g-+',linewidth=2,label='MINIGD')
plt.plot(theta_path_bgd[:,0],theta_path_bgd[:,1],'b-o',linewidth=3,label='BGD') # 全样本
plt.legend(loc='upper left')
plt.axis([3.5,4.5,2.0,4.0])
plt.show()
# 实际当中用minibatch比较多,一般情况下选择batch数量应当越大越好。
LR学习率(步长):对结果会产生巨大影响,上式中α即LR,一般初始为0.01,无法容忍的小,批处理batch数量一般选择32,64或128:
梯度下降该监督算法的优化目标为:
其中m是样本数,首先从utils.features导入prepare_for_training模块,处理数据标准化流程和特征变换。
# linear_regression.py
import numpy as np
from utils.features import prepare_for_training
class LinearRegression:
def __init__(self,data,labels,polynomial_degree = 0,sinusoid_degree = 0,normalize_data=True):
"""
Standardize data preprocessing operations
Obtain the number of all features
Initialize parameter matrix
"""
(data_processed, features_mean, features_deviation) = prepare_for_training(data, polynomial_degree, sinusoid_degree,normalize_data=True)
self.data = data_processed
self.labels = labels
self.features_mean = features_mean
self.features_deviation = features_deviation
self.polynomial_degree = polynomial_degree
self.sinusoid_degree = sinusoid_degree
self.normalize_data = normalize_data
num_features = self.data.shape[1] # num of features/variables
self.theta = np.zeros((num_features,1)) # bias term
def train(self,alpha,num_iterations = 500):
"""
Main function, gradient descent algorithm
par: learn rate, num_iteration
"""
cost_history = self.gradient_descent(alpha,num_iterations)
return self.theta,cost_history
def gradient_descent(self,alpha,num_iterations):
"""
actual iteration module will iterate num_iterations times
"""
cost_history = []
for _ in range(num_iterations):
self.gradient_step(alpha) # Implement parameter updates every iteration
cost_history.append(self.cost_function(self.data,self.labels))
return cost_history
def gradient_step(self,alpha):
"""
Parameter update module
"""
num_examples = self.data.shape[0]
prediction = LinearRegression.hypothesis(self.data,self.theta)
delta = prediction - self.labels
theta = self.theta
theta = theta - alpha*(1/num_examples)*(np.dot(delta.T,self.data)).T
self.theta = theta
def cost_function(self,data,labels):
"""
Loss function calculation module: J(theta)
It can also be MAE or MSE
"""
num_examples = data.shape[0]
delta = LinearRegression.hypothesis(self.data,self.theta) - labels
cost = (1/2)*np.dot(delta.T,delta)/num_examples
return cost[0][0]
@staticmethod
def hypothesis(data,theta):
predictions = np.dot(data,theta)
return predictions
def get_cost(self,data,labels):
data_processed = prepare_for_training(data, self.polynomial_degree, self.sinusoid_degree, self.normalize_data)[0]
return self.cost_function(data_processed,labels)
def predict(self,data):
"""
Use the trained parameter model to predict the regression value results
"""
data_processed = prepare_for_training(data, self.polynomial_degree, self.sinusoid_degree, self.normalize_data)[0]
predictions = LinearRegression.hypothesis(data_processed,self.theta)
return predictions
# MultivariateRegression.py
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.datasets import load_diabetes # https://archive.ics.uci.edu
from sklearn.model_selection import train_test_split
from linear_regression import LinearRegression
diabetes = load_diabetes()
data = pd.DataFrame(diabetes.data, columns=diabetes.feature_names)
data['target'] = diabetes.target
X_train, X_test, y_train, y_test = train_test_split(data.iloc[:,:len(diabetes.feature_names)], data.iloc[:,len(diabetes.feature_names):], train_size= 0.2, test_size=0.2, random_state=42)
# sns.pairplot(X_train+y_train, hue='target', palette='viridis')
# plt.show()
num_iterations = 500
learning_rate = 0.01
linear_regression = LinearRegression(X_train, y_train, polynomial_degree = 0, sinusoid_degree = 0)
(theta, cost_history) = linear_regression.train(learning_rate, num_iterations)
print(f"start loss: {cost_history[0]}")
print(f"end loss: {cost_history[-1]}")
plt.plot(range(num_iterations), cost_history)
plt.xlabel('Iterations')
plt.ylabel('Cost')
plt.title('Gradient Descent Progress')
plt.show()
y_predictions = linear_regression.predict((X_test))
print(f"{np.hstack((y_predictions,y_test))}")
# BivariateRegression.py
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import plotly
import plotly.graph_objs as go
from sklearn.datasets import load_diabetes
plotly.offline.init_notebook_mode()
from linear_regression import LinearRegression
diabetes = load_diabetes()
data = pd.DataFrame(diabetes.data, columns=diabetes.feature_names)
data['target'] = (diabetes.target-np.mean(diabetes.target))/np.std(diabetes.target)
data =data[['age','bmi','target']]
train_data = data.sample(frac=0.8)
test_data = data.drop(train_data.index)
input_param_name_1 = 'age'
input_param_name_2 = 'bmi'
output_param_name = 'target'
x_train = train_data[[input_param_name_1, input_param_name_2]].values
y_train = train_data[[output_param_name]].values
x_test = test_data[[input_param_name_1, input_param_name_2]].values
y_test = test_data[[output_param_name]].values
# Configure the plot with training dataset.
plot_training_trace = go.Scatter3d(
x=x_train[:, 0].flatten(),
y=x_train[:, 1].flatten(),
z=y_train.flatten(),
name='Training Set',
mode='markers',
marker={
'size': 10,
'opacity': 1,
'line': {
'color': 'rgb(255, 255, 255)',
'width': 1
},
}
)
plot_test_trace = go.Scatter3d(
x=x_test[:, 0].flatten(),
y=x_test[:, 1].flatten(),
z=y_test.flatten(),
name='Test Set',
mode='markers',
marker={
'size': 10,
'opacity': 1,
'line': {
'color': 'rgb(255, 255, 255)',
'width': 1
},
}
)
plot_layout = go.Layout(
title='Date Sets',
scene={
'xaxis': {'title': input_param_name_1},
'yaxis': {'title': input_param_name_2},
'zaxis': {'title': output_param_name}
},
margin={'l': 0, 'r': 0, 'b': 0, 't': 0}
)
plot_data = [plot_training_trace, plot_test_trace]
plot_figure = go.Figure(data=plot_data, layout=plot_layout)
plotly.offline.plot(plot_figure)
num_iterations = 500
learning_rate = 0.01
polynomial_degree = 0
sinusoid_degree = 0
linear_regression = LinearRegression(x_train, y_train, polynomial_degree, sinusoid_degree)
(theta, cost_history) = linear_regression.train(
learning_rate,
num_iterations
)
print('开始损失',cost_history[0])
print('结束损失',cost_history[-1])
plt.plot(range(num_iterations), cost_history)
plt.xlabel('Iterations')
plt.ylabel('Cost')
plt.title('Gradient Descent Progress')
plt.show()
predictions_num = 10
x_min = x_train[:, 0].min()
x_max = x_train[:, 0].max()
y_min = x_train[:, 1].min()
y_max = x_train[:, 1].max()
x_axis = np.linspace(x_min, x_max, predictions_num)
y_axis = np.linspace(y_min, y_max, predictions_num)
x_predictions = np.zeros((predictions_num * predictions_num, 1))
y_predictions = np.zeros((predictions_num * predictions_num, 1))
x_y_index = 0
for x_index, x_value in enumerate(x_axis):
for y_index, y_value in enumerate(y_axis):
x_predictions[x_y_index] = x_value
y_predictions[x_y_index] = y_value
x_y_index += 1
z_predictions = linear_regression.predict(np.hstack((x_predictions, y_predictions)))
plot_predictions_trace = go.Scatter3d(
x=x_predictions.flatten(),
y=y_predictions.flatten(),
z=z_predictions.flatten(),
name='Prediction Plane',
mode='markers',
marker={
'size': 1,
},
opacity=0.8,
surfaceaxis=2,
)
plot_data = [plot_training_trace, plot_test_trace, plot_predictions_trace]
plot_figure = go.Figure(data=plot_data, layout=plot_layout)
plotly.offline.plot(plot_figure)
梯度下降是核心解决方案,不光在线性回归中能用上,还有其他算法中能用上,比如神经网络。
可能遇到的问题:步长应当适当,不应过长或过小。学习率应当尽可能小,随着迭代的进行应当越来越小。在实际中,机器学习只会存在寻找全局最低点(最优点),不会存在局部最低点。
标准化和归一化都是数据预处理中常用的特征缩放方法,用于解决不同特征量纲(单位)或数值范围差异过大的问题,从而提升机器学习模型的性能(如梯度下降收敛更快、距离类模型效果更好)。但其目标和适用场景不同:
Standardization:将数据转换为均值为 0、标准差为 1 的分布(服从标准正态分布).
特点
-
不改变数据分布(仅平移和缩放),适用于数据本身近似正态分布(或假设模型要求数据符合正态分布,如线性回归、逻辑回归、SVM、PCA等)。
-
对异常值敏感(因为均值和标准差受极端值影响)。
-
适用算法:
-
基于距离的模型(如 KNN、K-Means、SVM)。
-
神经网络(加速梯度下降)。
-
主成分分析(PCA)。
-
Normalization:将数据缩放到固定范围(如 [0, 1] 或 [-1, 1])。
1. Min-Max 归一化(最常用):
2. 均值归一化(Mean Normalization):
特点
-
改变数据分布,将所有特征压缩到同一量纲。
-
对异常值敏感(因为最大最小值可能被极端值拉偏)。
-
适用算法:
-
图像处理(像素值归一化到 [0, 1])。
-
神经网络(输入数据需归一化)。
-
需要固定范围的模型(如聚类、某些距离算法)。
-
-
优先标准化:
-
数据分布近似正态分布。
-
使用基于距离或梯度的模型(如 SVM、KNN、神经网络)。
-
-
优先归一化:
-
数据边界明确(如像素值[0,255])。
-
需要固定输入范围的模型(如深度学习)。
-
-
其他情况:
-
如果数据有极端值,可以尝试 Robust Scaling(基于中位数和四分位数,对异常值不敏感)。
-
模型评估:
import numpy as np
import os
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
plt.rcParams['axes.labelsize'] = 14
plt.rcParams['xtick.labelsize'] = 12
plt.rcParams['ytick.labelsize'] = 12
import warnings
warnings.filterwarnings('ignore')
np.random.seed(42)
from sklearn.datasets import fetch_openml
mnist = fetch_openml(name='mnist_784', version=1) # sklearn找一个数据集,图像识别的28x28灰度图
X, y = mnist["data"], mnist["target"]
X.shape # (70000, 784)
y.shape # (70000, 1)
X_train, X_test, y_train, y_test = X[:60000], X[60000:], y[:60000], y[60000:] # 划分数据集
shuffle_index = np.random.permutation(60000)# 洗牌操作,生成随机排序的array
X_train, y_train = X_train.iloc[shuffle_index], y_train.iloc[shuffle_index]
为检验模型在不同数据集上的表现,确保模型的稳健性和泛化能力,常使用交叉验证技术评估机器学习模型表现:
-
K折交叉验证(K-Fold Cross-Validation):
-
将数据集随机划分为
k
个子集,通常k
取值为 5 或 10。 -
对于每一轮验证,选择一个子集作为验证集,剩余
k-1
个子集作为训练集,重复k
次。 -
最终结果是
k
次评估的平均值。
-
用K折交叉验证二分类任务:
y_train_5 = (y_train=='5') # bool值
y_test_5 = (y_test=='5') # bool值
# https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.SGDClassifier.html#sklearn.linear_model.SGDClassifier
from sklearn.linear_model import SGDClassifier # 梯度下降训练
sgd_clf = SGDClassifier(max_iter=5,random_state=42)
sgd_clf.fit(X_train,y_train_5)
sgd_clf.predict([X.iloc[35000]]) # Flase # X.iloc[35000]1维 入参要求2维,成为数组
y[35000] # 1
from sklearn.model_selection import cross_val_score # cv交叉验证次数
cross_val_score(sgd_clf,X_train,y_train_5,cv=3,scoring='accuracy') # 评估方法用准确率
#array([0.9502 , 0.96565, 0.96495]) # 结果差异相对较大
如果想要验证步骤,首先从sklearn寻找切分数据集的函数:
from sklearn.model_selection import StratifiedKFold
from sklearn.base import clone
skflods = StratifiedKFold(n_splits=3,random_state=42,shuffle=True)
for train_index,test_index in skflods.split(X_train,y_train_5):
clone_clf = clone(sgd_clf) # 用克隆方法确保每次数据一致
X_train_folds = X_train.iloc[train_index]
y_train_folds = y_train_5.iloc[train_index]
X_test_folds = X_train.iloc[test_index]
y_test_folds = y_train_5.iloc[test_index]
clone_clf.fit(X_train_folds,y_train_folds)
y_pred = clone_clf.predict(X_test_folds)
n_correct = sum(y_pred == y_test_folds)
print(n_correct/len(y_pred)) # 0.9502 0.96565 0.96495
Confusion Matrix-混淆矩阵:
i-th row and j-th column entry indicates the number of samples with true label being i-th class and predicted label being j-th class. True or Flase is fact, positive or negative is predicted.
from sklearn.model_selection import cross_val_predict
y_train_pred = cross_val_predict(sgd_clf,X_train,y_train_5,cv=3)
y_train_pred.shape # 60000,1
X_train.shape # (60000, 784)
from sklearn.metrics import confusion_matrix
confusion_matrix(y_train_5,y_train_pred)
#array([[53825, 754],
# [ 1314, 4107]], dtype=int64)
一个完美的分类器应该只有**true positives** 和 **true negatives**, 即主对角线元素不为0,其余元素为0.
多项式回归:随机模拟数据
m = 100
X = 6*np.random.rand(m,1) - 3
y = 0.5*X**2+X+np.random.randn(m,1)
plt.plot(X,y,'b.')
plt.xlabel('X_1')
plt.ylabel('y')
plt.axis([-3,3,-5,10])
plt.show()
from sklearn.preprocessing import PolynomialFeatures # 生成一个由所有多项式组合组成的新特征矩阵 的度数小于或等于指定度数的要素
poly_features = PolynomialFeatures(degree = 2,include_bias = False) # 特征变换 无偏置项
X_poly = poly_features.fit_transform(X)
X[0] # array([2.82919615])
X_poly[0] # array([2.82919615, 8.00435083])
from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()
lin_reg.fit(X_poly,y) # 多特征回归
print (lin_reg.coef_) # [[1.10879671 0.53435287]]
print (lin_reg.intercept_) # [-0.03765461]
X_new = np.linspace(-3,3,100).reshape(100,1)
X_new_poly = poly_features.transform(X_new)
y_new = lin_reg.predict(X_new_poly)
plt.plot(X,y,'b.')
plt.plot(X_new,y_new,'r--',label='prediction')
plt.axis([-3,3,-5,10])
plt.legend()
plt.show()
from sklearn.pipeline import Pipeline # 管道实验 流水线 # 多组实验对比
from sklearn.preprocessing import StandardScaler
plt.figure(figsize=(12,6))
for style,width,degree in (('g-',1,100),('b--',1,2),('r-+',1,1)): # 多项式最高次数
poly_features = PolynomialFeatures(degree = degree,include_bias = False) # 生成新特征
std = StandardScaler()
lin_reg = LinearRegression()
polynomial_reg = Pipeline([('poly_features',poly_features),
('StandardScaler',std),
('lin_reg',lin_reg)])
polynomial_reg.fit(X,y) # 先生成新特征,再进行标准化,最后回归
y_new_2 = polynomial_reg.predict(X_new) # 用测试数据进行预测
plt.plot(X_new,y_new_2,style,label = 'degree '+str(degree),linewidth = width)
plt.plot(X,y,'b.')
plt.axis([-3,3,-5,10])
plt.legend()
plt.show()
特征变换的越复杂(如degree=100),得到的结果过拟合风险越高,训练集得到的效果较好,但是测试集表现不好,不建议做的特别复杂。
过拟合解决:
数据样本数量对结果的影响 选用均方差MSE评估函数
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split # 数据集划分
def plot_learning_curves(model,X,y):
X_train, X_val, y_train, y_val = train_test_split(X,y,test_size = 0.2,random_state=100)
train_errors,val_errors = [],[] # 评估需要测试集训练集的真实集和预测集
for m in range(1,len(X_train)):# 用样本的数量
model.fit(X_train[:m],y_train[:m])
y_train_predict = model.predict(X_train[:m])
y_val_predict = model.predict(X_val)
train_errors.append(mean_squared_error(y_train[:m],y_train_predict[:m]))
val_errors.append(mean_squared_error(y_val,y_val_predict))
plt.plot(np.sqrt(train_errors),'r-+',linewidth = 2,label = 'train_error') # 采用RMSE,MSE波动幅度较大
plt.plot(np.sqrt(val_errors),'b-',linewidth = 3,label = 'val_error')
plt.xlabel('Trainsing set size')
plt.ylabel('RMSE') # 越小越好
plt.legend()
# 实际和预测之间的差值越小,过拟合风险降低
lin_reg = LinearRegression()
plot_learning_curves(lin_reg,X,y)
plt.axis([0,80,0,3.3])
plt.show()
数据量越少,训练集的效果会越好,但是实际测试效果很一般。实际做模型的时候需要参考测试集和验证集的效果。
多项式回归的过拟合风险
polynomial_reg = Pipeline([('poly_features',PolynomialFeatures(degree = 25,include_bias = False)),
('lin_reg',LinearRegression())])
plot_learning_curves(polynomial_reg,X,y)
plt.axis([0,80,0,5])
plt.show()
多项式越复杂越过拟合。
正则化:对权重参数进行惩罚,让权重参数尽可能平滑一些,有两种不同的方法来进行正则化惩罚,专门解决过拟合的问题:
前半部分是均方误差损失,后半部分为正则损失(由权重参数带来的不稳定损失),损失越小越好。
L2正则化(岭回归),惩罚项是参数的平方和,权重趋于0但不为0,使用处理多重共线性问题,约束是一个圆形,解的稳定性较好:
加了一个平方项
from sklearn.linear_model import Ridge # 岭回归
np.random.seed(42)
m = 20
X = 3*np.random.rand(m,1)
y = 0.5 * X +np.random.randn(m,1)/1.5 +1
X_new = np.linspace(0,3,100).reshape(100,1)
# polynomial 是否创建一个多项式的特征方程
def plot_model(model_calss,polynomial,alphas,**model_kargs):
for alpha,style in zip(alphas,('b-','g--','r:')):
model = model_calss(alpha,**model_kargs) # alphas=0 不进行正则化 loss=MSE
if polynomial:
model = Pipeline([('poly_features',PolynomialFeatures(degree =10,include_bias = False)), ('StandardScaler',StandardScaler()), ('lin_reg',model)])
model.fit(X,y)
y_new_regul = model.predict(X_new)# 正则化的预测
lw = 2 if alpha > 0 else 1
plt.plot(X_new,y_new_regul,style,linewidth = lw,label = 'alpha = {}'.format(alpha))
plt.plot(X,y,'b.',linewidth =3)
plt.legend()
plt.figure(figsize=(14,6))
plt.subplot(121)
plot_model(Ridge,polynomial=False,alphas = (0,10,100))
plt.subplot(122)
plot_model(Ridge,polynomial=True,alphas = (0,10**-5,1))
plt.show()
蓝色的线浮动大,不稳定性越大,不希望模型很复杂。惩罚力度越大,alpha值越大的时候,得到的决策方程越平稳。
L1正则化(lasso回归):惩罚项是参数的绝对值之和,让一些权重变成0实现特征选择,使用高维稀疏数据,约束是一个菱形,
from sklearn.linear_model import Lasso
plt.figure(figsize=(14,6))
plt.subplot(121)
plot_model(Lasso,polynomial=False,alphas = (0,0.1,1))
plt.subplot(122)
plot_model(Lasso,polynomial=True,alphas = (0,10**-1,1))
plt.show()
相对于岭回归效果不是很明显,注意α值的设置。
逻辑回归(二分类算法,决策边界可以非线性):
sigmod函数(非线性变化):
预测函数(其中):
红色为其自身图像,蓝色是其导数图像,自变量取值任意实数,值域[0,1],将任意输入映射到区间[0,1],在线性回归中得到一个预测值,再将该值映射到sigmod函数中,就完成值到概率的转换。
分类任务:
求解最大似然函数转换成对数:
用梯度上升最大值,但一般损失是越小越好,引入转换成梯度下降任务。
参数更新:
逻辑回归也适用于多分类任务,此时用到softmax求解。
三个类别需要三个决策边界,而不是两个决策边界,每一个样本点所属类别均需要用一个决策边界进行区分,一个决策边界只能区分两个类别,A V B, B V C, A V C均需要区分需要三个决策边界。
聚类(二分类、多分类)
贝叶斯算法
支持向量机SVM
决策树
神经网络
CV
CNN
RNN
GAN
LSTM
Transformer
数据校验规则
大数据迁移
业务分析
数据开发(字段,报表,归因,口径,取数验证,系统部署)
数据分析