简单的线性回归的实现以及向量化的实现
简单的线性回归的实现
首先我们在notebook上先加载上相应的库
import numpy as np
import matplotlib.pyplot as plt
我们先使用自己设计的一个假的数据来实验
x = np.array([1,2,3,4,5])
y = np.array([1,3,2,3,5])
在将x和y都设计出来以后,我们再用散点图的方式将这个数据集可视化出来
plt.scatter(x,y)
plt.axis([0,6,0,6])
可视化以后的图片如下
然后我们推导一下这个公式,看看是如何实现的
我们首先是计算x和y相应的均值
x_mean = np.mean(x)
y_mean = np.mean(y)
然后我们开始具体的计算a和b,重点是a的计算,从公式来看,a相当于由一个分子一个分母构成的,分子分母部分都是求和得出的,将分子设为num,分母为d,进行初始化,都初始化为0
num = 0.0
d = 0.0
在循环中,我们每次都从x和y中取出一个值,我们设这个值为x_i和y_i,使用zip方式将这两个放在一起,每次都各取一个值,然后分子分母部分就按照公式计算
for x_i,y_i in zip(x,y):
num += (x_i - x_mean) * (y_i - y_mean)
d += (x_i - x_mean) ** 2
那么a就是分子除以分母
a = num /d
相应的,b就是用y的均值减去a乘上x的均值
b = y_mean - a * x_mean
a和b如下
我们可以将这条直线可视化出来,我们可以设其为y_hat
y_hat = a * x + b
在绘制方面,首先我们使用散点图的方式将数据绘制出来,然后我们使用plot将直线再绘制出来,设其颜色为红色,坐标轴范围0-6
plt.scatter(x,y)
plt.plot(x,y_hat,color='r')
plt.axis([0,6,0,6])
图像如下图所示
现在,如果来了一个新的x
x_predict = 6
那么y就等于ax+b
y_predict = a * x_predict + b
预测结果可得
y_predict
那么我们可以在python chame中写出属于自己的简单的线性回归算法,思想与上面无异
代码如下
import numpy as np
from metrics import r2_score
class SimpleLinearRegression1:
def __init__(self):
"""初始化Simple linear Regression模型"""
self.a_ = None
self.b_ = None
def fit(self, x_train, y_train):
"""根据训练数据集x_train,y_train训练Simple Linear Regression模型"""
assert x_train.ndim == 1, \
"simple Linear Regression can only solve single"
assert len(x_train) == len(y_train), \
"the size of x train must be equal to the size of y_train"
x_mean = np.mean(x_train)
y_mean = np.mean(y_train)
num = 0.0
d = 0.0
for x_i, y_i in zip(x_train, y_train):
num += (x_i - x_mean) * (y_i - y_mean)
d += (x_i - x_mean) ** 2
self.a_ = num / d
self.b_ = y_mean - self.a_ * x_mean
return self
def predict(self, x_predict):
"""给定待预测数掘集x_predict,返同表示x_predict的结果向量"""
assert x_predict.ndim == 1, \
"simple Linear Regression can only solve single feature training data."
assert self.a_ is not None and self.b_ is not None, \
"must fit before predict! "
return np.array([self._predict(x) for x in x_predict])
def _predict(self, x_single):
"给定单个待预测数据x_single,返同x_single的阿测结果值"""
return self.a_ * x_single + self.b_
def score(self, x_test, y_test):
"""根据给定的测试数据集确定当前的模型"""
y_predict = self.predict(x_test)
return r2_score(y_test, y_predict)
def __repr__(self):
return "SimpleLinearRegression1()"
使用自己的算法
首先我们在notebook中引用自己写好的
from SimpleLinearRegression import SimpleLinearRegression1
然后我们对其进行实例化
reg1 = SimpleLinearRegression1()
然后我们fit一下x和y就可以了
reg1.fit(x,y)
之后我们进行预测,因为要求是一个数组,因此使用np.array进行包装一下
reg1.predict(np.array([x_predict]))
然后我们可以看一下具体学到的a和b的参数
reg1.a_
reg1.b_
然后我们再绘制一下这条直线,方法很简单,和上面操作没有什么差别,依然是设置直线,绘制散点图,再绘制出直线即可
y_hat1 = reg1.predict(x)
plt.scatter(x,y)
plt.plot(x,y_hat1,color='r')
plt.axis([0,6,0,6])
图像如下
我们如果想使其效率更高一些的话,可以使用向量化这个技巧
向量化的实现
上面我们使用循环的方式将a给求出来的,使用for循环的话,性能是不高的,如果可以将其改成向量的运算,那么性能将会有一定的提升的
我们可以将上述的a变换成两个向量之间的计算
对于这个式子,我们是可以不使用for循环一直计算的
w可以表示成(w1,w2,…,wm)
v可以表示成(v1,v2,…,vm)
那么其就变成了w点乘v
这样我们就可以直接使用numpy中向量的运算
这样的性能是比for循环高很多的
实现方面
我们先将SimpleLinearRegression中的代码进行修改,增添一个类SimpleLinearRegression2,将其修改成向量的运算方式,我们只需要改变fit函数的逻辑就可以,将num分子改成点乘的方式,由于出来的是一个数,那么我们使用.dot的方法,对d也同理
代码如下
import numpy as np
from metrics import r2_score
class SimpleLinearRegression2:
def __init__(self):
"""初始化Simple linear Regression模型"""
self.a_ = None
self.b_ = None
def fit(self, x_train, y_train):
"""根据训练数据集x_train,y_train训练Simple Linear Regression模型"""
assert x_train.ndim == 1, \
"simple Linear Regression can only solve single"
assert len(x_train) == len(y_train), \
"the size of x train must be equal to the size of y_train"
x_mean = np.mean(x_train)
y_mean = np.mean(y_train)
num = (x_train - x_mean).dot(y_train - y_mean)
d = (x_train - x_mean).dot(x_train - x_mean)
self.a_ = num / d
self.b_ = y_mean - self.a_ * x_mean
return self
def predict(self, x_predict):
"""给定待预测数掘集x_predict,返同表示x_predict的结果向量"""
assert x_predict.ndim == 1, \
"simple Linear Regression can only solve single feature training data."
assert self.a_ is not None and self.b_ is not None, \
"must fit before predict! "
return np.array([self._predict(x) for x in x_predict])
def _predict(self, x_single):
"给定单个待预测数据x_single,返同x_single的结果值"""
return self.a_ * x_single + self.b_
def score(self, x_test, y_test):
"""根据给定的测试数据集确定当前的模型"""
y_predict = self.predict(x_test)
return r2_score(y_test, y_predict)
def __repr__(self):
return "SimpleLinearRegression2()"
那么我们在notebook中来实现一下
首先还是引用新的类
from SimpleLinearRegression import SimpleLinearRegression2
然后实例化,然后操作同上
reg2 = SimpleLinearRegression2()
reg2.fit(x,y)
reg2.a_
reg2.b_
同样绘制一个图片用来可视化
y_hat2 = reg2.predict(x)
plt.scatter(x,y)
plt.plot(x,y_hat2,color='r')
plt.axis([0,6,0,6])
图像如下
向量化实现的性能测试
我们通过创建一个相对于更大的数据,进而再创建一个向量big_x,然后我们再创建一个big_y,使其与big_x产生一个线性关系,但是我们不能让其完全呈现出,在后面添加一个符合正态分布的一个噪音np.random.normal(size=m)
m = 1000000
big_x = np.random.random(size=m)
big_y = big_x * 2.0 + 3.0 + np.random.normal(size=m)
然后我们测试一下两个版本所对应的性能,通过对比就可以知道两者的性能差距是多少
%timeit reg1.fit(big_x,big_y)
%timeit reg2.fit(big_x,big_y)
结果如下
很明显,向量带来的性能提升是很巨大的,所以一般可以向量化就要向量化,不过要基于公式的基础上更深入才可以
然后我们看一下训练出来的两种a和b的情况
reg1.a_
reg1.b_
reg2.a_
reg2.b_
输出结果如下