1.超简易代码(不用sklearn)
可以改进的地方:导入函数没写,不过大部分默认使用 .csv的格式
import numpy as np
import matplotlib.pyplot as plt
train_x = np.array([0.5, 0.6, 0.8, 1.1, 1.4])
train_y = np.array([5.0, 5.5, 6.0, 6.8, 7.0])
test_x = np.array([0.45, 0.55, 1.0, 1.3, 1.5])
test_y = np.array([4.8, 5.3, 6.4, 6.9, 7.3])
lr=0.01
times=1000
#这里这么写是为了画图
w0=[2]
w1=[2]
epoches=[0]
losses=[0]
#认为估算回归结果是 y_pred=w0*x+w1
#loss=(w0*x+w1-y)**2/2
for i in range(1,times+1):
epoches.append(i)
#d0--对w0求偏导,d1--对w1求偏导
loss=((w0[-1]*train_x+w1[-1]-train_y)**2/2).sum()
losses.append(loss)
d0=(train_x*(w0[-1]*train_x+w1[-1]-train_y)).sum()
d1=(w0[-1]*train_x+w1[-1]-train_y).sum()
w0_new=w0[-1]-d0*lr
w1_new=w1[-1]-d1*lr
w0.append(w0_new)
w1.append(w1_new)
print('{:4}> w0={:.8f}, w1={:.8f}, loss={:.8f}'.format(epoches[-1], w0[-1], w1[-1], losses[-1]))
#作图
plt.subplot(313)
#加网线格子
plt.grid(linestyle=':')
plt.ylabel(r'$w0$',fontsize=14)
plt.plot(epoches,w0,c='blue',label=r'$w0$')
plt.legend()
plt.subplot(312)
plt.grid(linestyle=':')
plt.ylabel(r'$w1$',fontsize=14)
plt.plot(epoches,w1,c='dodgerblue',label=r'$w1$')
plt.legend()
plt.subplot(311)
plt.grid(linestyle=':')
plt.ylabel(r'$loss$',fontsize=14)
plt.plot(epoches,losses,c='orangered',label=r'$losses$')
plt.legend()
#作回归曲线
pred_test_y = w0[-1] + w1[-1] * test_x
plt.figure('Linear Regression', facecolor='lightgray')
plt.title('Linear Regression', fontsize=20)
plt.xlabel('x', fontsize=14)
plt.ylabel('y', fontsize=14)
plt.tick_params(labelsize=10)
plt.grid(linestyle=':')
plt.scatter(train_x, train_y, marker='s', c='dodgerblue', alpha=0.5, s=80, label='Training')
plt.scatter(test_x, test_y, marker='D', c='orangered', alpha=0.5, s=60, label='Testing')
plt.scatter(test_x, pred_test_y, c='orangered', alpha=0.5, s=80, label='Predicted')
plt.plot(test_x, pred_test_y, '--', c='limegreen', label='Regression', linewidth=1)
plt.legend()
plt.show()
plt.show()
#带入测试库
loss=((w0[-1]*test_x+w1[-1]-test_y)**2/2).sum()
print('带入测试:y_pred={:.8f}*x+{:.8f}, loss={:.8f}'.format( w0[-1], w1[-1], loss))
2.用相关API写
难点在于传的格式
方法:
import sklearn.linear_model as lm
# 创建模型
model = lm.LinearRegression()
# 训练模型
# 输入为一个二维数组表示的样本矩阵
# 输出为每个样本最终的结果
model.fit(输入, 输出) # 通过梯度下降法计算模型参数
# 预测输出
# 输入array是一个二维数组,每一行是一个样本,每一列是一个特征。
result = model.predict(array)
#线性回归
import numpy as np
import sklearn.linear_model as lm
import matplotlib.pyplot as plt
#训练
x, y = np.loadtxt('D:\BaiduNetdiskDownload\数据集 21.8\乔总结数据集\single.txt', delimiter=',', usecols=(0,1), unpack=True)
x = x.reshape(-1, 1)
model=lm.LinearRegression()
model.fit(x,y)
y_pred=model.predict(x)
#得到模型
#测试
test_x=(np.linspace(x.min(),x.max(),500)).reshape(-1,1)
y_test=model.predict(test_x)
#作图
plt.figure('Linear Regression',facecolor='lightgray')
plt.title('Linear Regression',fontsize=20)
plt.scatter(x,y,c='dodgerblue',alpha=0.75,s=60,label='sample')
plt.plot(test_x,y_test,c='orangered',label='regression')
plt.legend()
plt.show()
3.评估模块
import sklearn.metrics as sm
# 平均绝对值误差:1/m∑|实际输出-预测输出|
sm.mean_absolute_error(y, pred_y)
# 平均平方误差:SQRT(1/mΣ(实际输出-预测输出)^2)
sm.mean_squared_error(y, pred_y)
# 中位绝对值误差:MEDIAN(|实际输出-预测输出|)
sm.median_absolute_error(y, pred_y)
# R2得分,(0,1]区间的分值。分数越高,误差越小。
sm.r2_score(y, pred_y)
4. 保存与加载
import pickle
pickle.dump(内存对象, 磁盘文件) # 保存模型
model = pickle.load(磁盘文件) # 加载模型
eg.
# 将训练好的模型对象保存到磁盘文件中
with open('../../data/linear.pkl', 'wb') as f:
pickle.dump(model, f)
# 从磁盘文件中加载模型对象
with open('../../data/linear.pkl', 'rb') as f:
model = pickle.load(f)
# 根据输入预测输出
pred_y = model.predict(x)
5.岭回归
为避免噪声样本(脱离在大部队之外)的影响,这些样本认为是偶然性造物
岭回归相较于普通的线性回归在损失函数后加了一个正则项,减少了噪声样本的影响
详细版本:
普通线性回归模型使用基于梯度下降的最小二乘法,在最小化损失函数的前提下,寻找最优模型参数,于此过程中,包括少数异常样本在内的全部训练数据都会对最终模型参数造成程度相等的影响,异常值对模型所带来影响无法在训练过程中被识别出来。为此,岭回归在模型迭代过程所依据的损失函数中增加了正则项,以限制模型参数对异常样本的匹配程度,进而提高模型面对多数正常样本的拟合精度。
一些说明:
0.当P=2时下图即是岭回归的损失函数
1.正则强度越大,曲线的拟合度越低,忽略噪音的效果越好
方法:
import sklearn.linear_model as lm
# 创建模型
model = lm.Ridge(正则强度,fit_intercept=是否训练截距, max_iter=最大迭代次数)
# 训练模型
# 输入为一个二维数组表示的样本矩阵
# 输出为每个样本最终的结果
model.fit(输入, 输出)
# 预测输出
# 输入array是一个二维数组,每一行是一个样本,每一列是一个特征。
result = model.predict(array)
import sklearn.linear_model_ridge as lm
# 创建模型
model_ridge = lm.Ridge(正则强度,fit_intercept=是否训练截距, max_iter=最大迭代次数)
# 训练模型
# 输入为一个二维数组表示的样本矩阵
# 输出为每个样本最终的结果
model_ridge.fit(输入, 输出)
# 预测输出
# 输入array是一个二维数组,每一行是一个样本,每一列是一个特征。
result = model_ridge.predict(array)
'''
import numpy as np
import sklearn.linear_model as lm
import matplotlib.pyplot as plt
#训练
x, y = np.loadtxt('D:\BaiduNetdiskDownload\数据集 21.8\乔总结数据集\single.txt', delimiter=',', usecols=(0,1), unpack=True)
x = x.reshape(-1, 1)
model_ridge=lm.Ridge(100,fit_intercept=True,max_iter=800)
model_ridge.fit(x,y)
model_ridge.predict(x)
#得到模型
#测试
test_x=(np.linspace(x.min(),x.max(),500)).reshape(-1,1)
y_test=model_ridge.predict(test_x)
#训练
model_linear=lm.LinearRegression()
model_linear.fit(x,y)
model_linear.predict(x)
#得到模型
#测试
y_test_l=model_linear.predict(test_x)
#作图
plt.scatter(x,y,c='dodgerblue',alpha=0.75,s=60,label='sample')
plt.plot(test_x,y_test,c='orange',label='ridge regression')
plt.plot(test_x,y_test_l,c='red',label='linear regression')
plt.legend()
plt.show()
6.多项式回归
用多项式来进行更好的拟合
缺点:会过拟合
原理:y=w0 + w1 x + w2 x2 + w3 x3 + … + wd xd
将高次项看做对一次项特征的扩展得到:
y=w0 + w1 x1 + w2 x2 + w3 x3 + … + wd xd
故而一元多次转化为多元一次
所以一元多项式回归的实现需要两个步骤:
- 将一元多项式回归问题转换为多元线性回归问题(只需给出多项式最高次数即可)。
- 将1步骤得到多项式的结果中 w1 w2 … 当做样本特征,交给线性回归器训练多元线性模型。
方法:
import sklearn.pipeline as pl
import sklearn.preprocessing as sp
import sklearn.linear_model as lm
model = pl.make_pipeline(
sp.PolynomialFeatures(num), # 多项式特征扩展器,num为最高次数
lm.LinearRegression()) # 线性回归器
#多项式回归
import numpy as np
import sklearn.preprocessing as sp
import sklearn.metrics as sm
import sklearn.linear_model as lm
import sklearn.pipeline as pl
import matplotlib.pyplot as plt
#训练
x, y = np.loadtxt('D:\BaiduNetdiskDownload\数据集 21.8\乔总结数据集\single.txt', delimiter=',', usecols=(0,1), unpack=True)
x=x.reshape(-1,1)
model=pl.make_pipeline(sp.PolynomialFeatures(5),lm.LinearRegression())
model.fit(x,y)
y_pred=model.predict(x)
#得到模型
#测试
test_x=(np.linspace(x.min(),x.max(),500)).reshape(-1,1)
y_test=model.predict(test_x)
print(sm.r2_score(y,y_pred))
#作图
plt.figure('Polynomial Regression',facecolor='lightgray')
plt.title('Polynomial Regression',fontsize=20)
plt.scatter(x,y,c='dodgerblue',alpha=0.75,s=60,label='sample')
plt.plot(test_x,y_test,c='orangered',label='regression')
plt.legend()
plt.show()
一点说明:
7.pytorch手写版
import math
import time
import numpy as np
import torch
import matplotlib.pyplot as plt
def synthetic_data(w, b, num_examples_row):
x = torch.normal(0, 1, (num_examples_row, len(w)))
y = torch.matmul(x, w) + b
y += torch.normal(0, 0.01, y.shape)
return x, y.reshape((-1, 1))
w_in = torch.tensor([2, -3.4])
b_in = 4.2
features, labels = synthetic_data(w_in, b_in, 10000)
print(features)
x1 = features[:, 0]
x2 = features[:, 1]
y = labels
plt.scatter(x1, x2, c=y, cmap='jet', s=1)
def data_iter(batch_size, features, labels):
size = int(len(features))
# 乱序
indices = list(range(size))
for i in range(0, size, batch_size):
batch_indices = indices[i:min(i + batch_size,
size)]
m=max(batch_indices)
n=min(batch_indices)
features=features
yield features[n:m], labels[n:m]
def linreg(x, w, b):
#这里应该产出一个 10*1的y_hat
sum=torch.matmul(x,w)+b
return sum
def loss(y_hat,y):
l=(y_hat-y.reshape(y_hat.shape))**2
l.requires_grad_(True)
return l
def sgd(params,lr,batch_size):
#这个with torch.no_grad的作用来避免 ‘nonetype’的出现
with torch.no_grad():
for p in params:
rig = p.grad / batch_size
#不大明白为什么这里把p-改成p=p-lr*rig会报错
p-=lr * rig
# 这一步是把p的梯度记录清零,不然他会自动把下一次的也加上
p.grad.zero_()
return
batch_size=10
#features=torch.vstack((x1,x2))
#labels=y
width=2
w= torch.tensor(np.random.normal(1, 0.01, size=(2,1)), dtype=torch.float32,requires_grad=True)
b=torch.tensor(np.random.normal(1, 0.01, size=(1,1)), dtype=torch.float32,requires_grad=True)
print(b.size)
#w.requires_grad(True)
#b.requires_grad(True)
lr=0.001
#设置200 100%过拟合
num_epochs=200
net=linreg
size=int(len(y))
for x, y in data_iter(batch_size, features, labels):
y_hat = linreg(x, w, b)
l = loss(y_hat, y)
l.requires_grad_(True)
a = l.sum()
a.backward()
sgd([w, b], lr, batch_size)
l_plt=[]
times_plt=[]
time=0
for epoch in range(num_epochs):
for x, y in data_iter(batch_size, features, labels):
y_hat=linreg(x,w,b)
l=loss(y_hat,y)
l.requires_grad_(True)
a=l.sum()
a.backward()
sgd([w,b],lr,batch_size)
with torch.no_grad():
train_1=loss(linreg(features,w,b),labels)
l_plt.append(train_1.mean())
time=time+1
times_plt.append(time)
print(f'epoch{epoch+1},loss{float(train_1.mean())}')
plt.figure('Linear Regression', facecolor='lightgray')
plt.title('Linear Regression', fontsize=20)
plt.tick_params(labelsize=10)
plt.grid(linestyle=':')
times_plt=np.array(times_plt)
l_plt=np.array(l_plt)
plt.xlabel('epoch', fontsize=14)
plt.ylabel('loss', fontsize=14)
plt.plot(times_plt, l_plt,'--', c='limegreen', label='loss', linewidth=1)
plt.legend()
plt.show()
8.在pytorch上使用相关API
import torch
import numpy as np
from torch.utils import data
from torch import nn
w_in=torch.tensor([2,-3.4])
b_in=4.2
def synthetic_data(w, b, num_examples_row):
x = torch.normal(0, 1, (num_examples_row, len(w)))
y = torch.matmul(x, w) + b
y += torch.normal(0, 0.01, y.shape)
return x, y.reshape((-1, 1))
features,labels= synthetic_data(w_in,b_in,10000)
def load_array(data_arrays,batch_size,is_train=True):
#python数据迭代器
dataset=data.TensorDataset(*data_arrays)
#下面的擢用是每次随机从dataset里面(shuffle判断是都打乱)取出样本
return data.DataLoader(dataset,batch_size,shuffle=is_train)
batch_size=10
data_iter=load_array((features,labels),batch_size)
next(iter(data_iter))
#这个sequential可以理解为一个序列
net=nn.Sequential(nn.Linear(2,1))
#类似于之前的torch.normal_,normal表示正太分布
net[0].weight.data.normal_(0,0.01)
net[0].bias.data.fill_(0)
#均方误差
loss=nn.MSELoss()
#SGD
trainer=torch.optim.SGD(net.parameters(),lr=0.01)
num_epoch=200
for epoch in range(num_epoch):
for x,y in data_iter:
l=loss(net(x),y)
#梯度清零,不然会叠加
trainer.zero_grad()
l.backward()
#step,模型更新
trainer.step()
l=loss(net(features),labels)
print('epoch{:},loss{:}'.format(epoch+1,l))
可见效果跟手写版的差距不大