!!!!!学习自b站up主 积极向上的江上 的机器学习视频,所以以下为学习记录!!!!!
一、代码设计思路
首先要明确的是实战中的代码要实现的目标:使用线性回归拟合坐标轴上点,达到预测效果,那么在整个线性回归过程中包含loss函数(这里可以看出线性回归和loss函数是一个包含与被包含的关系)。其实这整个过程就像高中做过的统计题(只不过在机器学习中这个过程会更严谨一点)——根据题目给出的数据选择一个函数(简单题选择线性函数如y = ax+b就能做出来,复杂题选择指数函数之类才能做出来),然后用最小二乘法求回归直线方程中的a,b,最后得到你想要的函数,接着题目一般就会问“请你猜测以下xxxx的xx是多少”(这在机器学习中就叫预测)。
在视频中up主为了验证结果的准确性,一开始就告知我们一个已知函数y = 1.41*x + 0.89,然后将这个函数和一个正态分布函数进行结合得到一些数据,在函数中将其表达为加载数据。
我们再从中选取100个数据点定义Loss函数,当然代码中将loss函数的结果还除以了100,这其实没有影响,只是多了一个求w*x+b-y的平均值。代码中将每个点的的x,y坐标值存放在矩阵中,为了计算的时候能够分别得到全体x和全体y,以data[i,0]和data[i,1]的形式获得。
接着就是求w,b的梯度值,这个过程就是看作求导,如果把w*x+b-y看作二元函数f(w,b)(记住不是把x,y看作未知数了,因为x,y就是加载的数据点,现在未知的是w,b),那么loss函数就可以看作g(f(w,b)),那么g(f(w,b))对w,b求导就可以用链式法则啦!(学过高数的应该都知道什么是链式法则,其实就算不知道,也知道怎么操作,高中都有用过)。代码中除以100只是为了得到平均值。
那么接下来就可以更新w,b,这个更新需要根据前面计算出w,b梯度值来的,这里有一个固有更新公式(我觉得):w_new = w_old - lr*w_tidu,b_new = b_old - lr*b_tidu。这里的lr是学习率,用来控制更新的速度(我觉得),以防止错过loss的最低点,也即错过最佳拟合效果。
代码中一共更新了20000次,全部更新结束后就能得到一个较为准确的w,b,也即一个较为准确的模型,那么接下来我们就看看运行结果吧!
二、实战结果
从运行结果可以看出训练出的w,b和实际已知值(1.41,0.89)非常接近,这说明整个线性回归过程很顺利,这是一个合适的模型,接下来就可以根据得到的这个模型去预测点的值啦!
当然我们从运行结果也可以看出当w,b的梯度值越接近0,说明loss函数越接近最低点,也就说明距离我们想要的函数模型越接近。其实这个“梯度值越接近0”可以看作“斜率越接近0”,在函数求导中求导后的值即为这一点的斜率,当斜率为0时一般就达到了该函数的极值(也有例外,如y=x^3就不是这样了)。
三、总结
比起之前单纯学线性回归、Loss函数的理论,现在对这俩东西有了更更更更更深入的认识,至少不是“好像懂了但又好像没懂”的状态。对于小白来说这个视频简直太神了!!!可惜up主已经停更一年了,好可惜呀!!!!
关于梯度下降算法的讲解可参考这篇博客,对于有高数基础的理解起来就特别轻松: https://blog.csdn.net/qq_41800366/article/details/86583789?spm=1001.2014.3001.5506
最后附上我跟着视频敲出的完整代码以及我根据对代码的理解写出的注释
#加载数据
#写出损失函数
#计算梯度并不断更新
#求出the best参数
import numpy as np
w = 0 #w初值
b = 0 #0初值
lr = 0.0001 #设置学习率为0.0001
num = 20000 #迭代(更新次数)20000次
#1、加载数据
data = [] #先创建空数据列表
for i in range(100): #循环100次,即加载100个点
x = np.random.uniform(-10.,10.) #x值从-10到10中随机选取(包括小数)
eps = np.random.normal(0.,0.1) #偏置满足正态分布:期望0,标准差0.1
y = 1.41*x + 0.89 + eps #y值,设置实际上的w值为1.41,b为0.89
data.append([x,y]) #将x,y值以矩阵形式存入
data = np.array(data) #data中的内容转化为矩阵形式存储
#2、定义Loss函数
def loss():
loss = 0 #设置loss初值为0
for i in range(100): #循环100次,即将100个点都拿出来计算
x = data[i,0] #随着循环到100次可取出全体x值
y = data[i,1] #随着循环到100次可取出全体y值
#计算损失函数求和wx+b-y
loss += pow((w*x+b-y),2)/100
return loss
#3、定义梯度(简单来说就是分别对w,b求导)
def gradient():
w_gradient = 0 #设置w梯度初值为0
b_gradient = 0 #设置b梯度初值为0
for i in range(100): #将100个点都取出分别计算出它们对应的梯度
x = data[i,0] #随着循环到100次可取出全体x值
y = data[i,1] #随着循环到100次可取出全体y值
#计算梯度
w_gradient += (2/100)*(w*x+b-y)*x #将loss函数对w求导就可以得出这行公式
#w*x+b-y理解为二元函数f(w,b),再利用链式法则求导
b_gradient += (2/100)*(w*x+b-y) #将loss函数对b求导就可以得出这行公式
return w_gradient, b_gradient #函数返回100次计算后的w,b梯度值
#4、更新num次梯度以及w和b
for j in range(num):
w_gradient = 0 #设置w梯度初值为0
b_gradient = 0 #设置b梯度初值为0
w_gradient,b_gradient = gradient() #调用定义梯度函数grandient(),见上
#梯度不断更新
#w(i+1) = w(i) - lr*dl/dw
#b(i+1) = b(i) - lr*dl/db
w = w - (lr*w_gradient) #利用更新参数的固有公式(见前两行注释)来更新w
b = b - (lr*b_gradient) #利用更新参数的固有公式来更新b
#每循环100次就查看w,b梯度值更新情况
if j%100 ==0 :
print('w_grandient = ',w_gradient,'b_grandien = ',b_gradient)
print('w = ', w,'b = ', b) #全部结束后输出训练得到的w,b值
#从前面的运行结果可以看出与设置的实际w,b值近似