一元线性回归
一元线性回归函数
成本函数
成本函数描述了拟合曲线的y值和y的实际值的平均误差平方,用来衡量拟合曲线的对实际数据的解释程度,即预测曲线的准确率。形状是个U型函数。
梯度下降
w、b的取值影响成本函数J,在三维空间里,成本函数类似一个小平原,有多个局部极小值和极大值,也有等成本线(类似等高线),梯度下降实现的是从初始起点开始找出最陡峭的方向(迈出一小步,成本以最快的速度下降),逐步达到成本的局部极小值。在不同的起点实行梯度下降得到的局部极小值可能不同。
线性回归的原理
梯度下降,通过反复迭代,找到使成本函数以最快的速度达到局部最小,对应的w,b值。
过程:
1、对w、b求偏导,求出使导数为0的w、b值,就得到使J沿w、b下降最快的方向。
2、学习率定义梯度下降的步长,更新w,b值;更新的w、b值代入方程求解成本函数J,再对新的J函数求偏导,得到新的w,b.....达到局部最小值的时候到导数为0,w、b不再更新。
回归效果检验
观察学习曲线(迭代次数为x轴,J成本为y轴)是否递减且收敛(㇏)
学习率的选择
通过观察学习曲线的形状判断学习率的选取是否合适。
①学习率太大。每次移动的步长越来越大,成本J增加,学习曲线发散(丿)
②学习率太小。需要大量迭代才能学习函数才能收敛,耗费时间长。此外,太小的学习率也有可能使成本J上升(有窃听器)。
正确的学习率调试方式,选择一个较小的a开始,观察成本函数J是否在每一次迭代中降低。
学习速率的一系列常用值:... 0.001 0.01 0.1 1 ...
(吴恩达)通常先从0.001开始,然后提高三倍,0.003,然后0.01,0.03,0.1,0.3,1,3
学习曲线观察
①发散。可能是因为a的值太大,或者是符号写成了+号。导致每次迭代成本增加。
②
代码实现
①简单一元线性回归(无梯度下降)
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
# x_train为自变量,y_train为因变量
'''准备数据'''
x_train = np.array([1.0, 2.0])
y_train = np.array([300.0, 500.0])
print(f"x_train = {x_train}")
print(f"y_train = {y_train}")
'''计算样本量'''
print(f"x的维度形状: {x_train.shape}")
m = x_train.shape[0] #shape[0]是指数组在第一个维度的大小,一维数组第一个维度是数组大小,二维数组shape[0]就是行数
print(f"样本量: {m}")
# m为样本量
m = len(y_train) #len()函数也可以
print(f"样本量: {m}")
'''输出其中一个样本'''
i = 1 # 样本下标
x_i = x_train[i]
y_i = y_train[i]
print(f"(x^({i}), y^({i})) = ({x_i}, {y_i})")
'''设置参数'''
w=100
b=100
print(f'w:{w}')
print(f'b:{b}')
'''定义回归函数'''
def compute_model_output(x,w,b):
m=x.shape[0]
f_wb=np.zeros(m)
for i in range(m):
f_wb[i]=w*x[i]+b
return f_wb
'''绘制回归曲线'''
tmp_f_wb=compute_model_output(x_train,w,b)
plt.plot(x_train,tmp_f_wb,c='b',label='Our prediction')
plt.scatter(x_train,y_train,marker='x',c='r',label='Actual Value') #设置数据点
plt.legend()
plt.title('price&size') #设置标题
plt.ylabel('price') #设置轴标签
plt.xlabel('size')
plt.savefig('./房价和面积关系.png')
'''预测值'''
w = 200
b = 100
x_i = 1.2
cost_1200sqft = w * x_i + b
print(f"面积{x_i}对应${cost_1200sqft:.0f}")
②成本函数
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
x=np.array([1,2])
y=np.array([300,500])
'''定义成本函数'''
def compute_cost(x,y,w,b):
m=x.shape[0]
cost_sum=0
for i in range(m):
f_wb=w*x[i]+b
cost_sum=cost_sum+(f_wb-y[i])**2
total_cost=(1/(2*m))*cost_sum
return total_cost
cost=compute_cost(x,y,0,100)
print(cost)
给定w,b,输出拟合曲线与实际值的均方误差(成本)。
③线性回归(梯度下降)
import math, copy
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
plt.style.use('./deeplearning.mplstyle')
from lab_utils_uni import plt_house_x, plt_contour_wgrad, plt_divergence, plt_gradients
'''训练集'''
x_train = np.array([1.0, 2.0])
y_train = np.array([300.0, 500.0])
'''成本函数'''
def compute_cost(x, y, w, b):
m = x.shape[0]
cost = 0
for i in range(m):
f_wb = w * x[i] + b
cost = cost + (f_wb - y[i]) ** 2
total_cost = 1 / (2 * m) * cost
return total_cost
'''梯度下降函数'''
def compute_gradient(x,y,w,b):
m=x.shape[0]
dj_dw=0
dj_db=0
for i in range(m):
f_wb=w*x[i]+b
dj_dw_i = (f_wb-y[i])*x[i]
dj_db_i = (f_wb - y[i])
dj_dw += dj_dw_i
dj_db += dj_db_i
dj_dw = dj_dw / m
dj_db = dj_db / m
return dj_dw, dj_db
'''执行梯度下降来拟合w,b''' #通过使用num_iters梯度步长和学习率来更新w,b
def gradient_descent(x,y,w_in,b_in,alpha,num_iters,cost_function,gradient_function):
'''
参数 x--训练集输入特征
y--训练集结果
w_in--初始w值
b_in--初始b值
alpha--学习率
num_iters--运行梯度下降的迭代次数
cost_function--调用成本函数计算当前w,b值产生的成本
gradient_function--调用梯度下降函数更新w,b
返回 w--更新后的w
b--更新后的b
J_history--历史成本函数
P_history--历史(w,b)
'''
J_history=[]
P_history=[]
b=b_in
w=w_in
for i in range(num_iters):
dj_dw,dj_db=gradient_function(x,y,w,b)
b=b-alpha*dj_db
w=w-alpha*dj_dw
if i<100000: #如果迭代次数小于100000,算法会在每次迭代后计算并保存当前的成本函数值和参数值
J_history.append(cost_function(x,y,w,b))
P_history.append([w,b])
if i%math.ceil(num_iters/10)==0: #每完成总迭代次数的大约十分之一时,打印出当前的迭代次数、成本函数值、梯度以及参数值,监控迭代进展
print(f"Iteration {i:4}: Cost {J_history[-1]:0.2e} ",
f"dj_dw: {dj_dw: 0.3e}, dj_db: {dj_db: 0.3e} ",
f"w: {w: 0.3e}, b:{b: 0.5e}")
return w, b, J_history,P_history
'''进行梯度下降'''
w_init=0
b_init=0
itrations=2000
tmp_alpha=1.0e-1
w_final,b_final,J_hist,p_hist=gradient_descent(x_train,y_train,w_init,b_init,tmp_alpha,itrations,compute_cost,compute_gradient)
print(f"(w,b)经过梯度下降后,更新为:({w_final:8.4f},{b_final:8.4f})")
'''绘制学习曲线'''
fig,(ax1,ax2)=plt.subplots(1,2,constrained_layout=True,figsize=(12,4))
ax1.plot(J_hist[:1000]) #绘制前100次迭代的成本函数值。这通常用于观察算法在开始阶段的快速变化
ax2.plot(1000 + np.arange(len(J_hist[1000:])), J_hist[1000:]) #绘制第1000次迭代到最后的成本函数值。这有助于观察算法在后期是否平稳收敛。
ax1.set_title("Cost vs. iteration(start)") #设置标题
ax2.set_title("Cost vs. iteration (end)") #设置标题
ax1.set_ylabel('Cost')
ax1.set_xlabel('iteration step')
ax2.set_ylabel('Cost')
ax2.set_xlabel('iteration step')
plt.show()
'''预测'''
print(f"1000 平方米的房子预测需要 {w_final*1.0 + b_final:0.1f} 千美元")
print(f"1200 平方米的房子预测需要 {w_final*1.2 + b_final:0.1f} 千美元")
print(f"2000 平方米的房子预测需要 {w_final*2.0 + b_final:0.1f} 千美元")
'''绘制成本函数等高线--梯度下降路径'''
fig, ax = plt.subplots(1,1, figsize=(12, 6))
plt_contour_wgrad(x_train, y_train, p_hist, ax)
plt.show()
'''绘制成本函数等高线--梯度下降步长'''
fig, ax = plt.subplots(1,1, figsize=(12, 4))
plt_contour_wgrad(x_train, y_train, p_hist, ax, w_range=[180, 220, 0.5], b_range=[80, 120, 0.5],contours=[1,5,10,20],resolution=0.5)
plt.show()
'''反例:学习率太大'''
w_init = 0
b_init = 0
iterations = 10
tmp_alpha = 8.0e-1
w_final, b_final, J_hist, p_hist = gradient_descent(x_train ,y_train, w_init, b_init, tmp_alpha,
iterations, compute_cost, compute_gradient)
plt_divergence(p_hist,J_hist,x_train, y_train) #J太大,已溢出报错
plt.show()
多元线性回归
多元线性回归函数
其中,、
符号说明
——表示总样本数
——表示第个样本,表示为多个特征组合起来的列表
——表示第个特征
——表示第个样本的第个特征
矢量化
①非矢量化,逐一相乘汇总,若样本量大,编码和运行效率低。
import numpy as np
w=np.array([1,2.5,-3.3])
b=4
x=np.array([10,20,30])
f=w[0]*x[0]+w[1]*x[1]+w[2]*x[2]+b
②非矢量化,使用for循环,运行效率低,运行n-1次得到结果逐一累加。
f=0
for j in range(0,n):
f=f+w[j]*x[j]
f=f+b
③矢量化,使用dot()函数,运行效率高,只需两步(w和x同时相乘,然后一步汇总)。
f=np.dot(w,x)+b
梯度下降
法方程
只适用于线性回归,特征数量多的时候也很慢,也叫正态方程法。
Numpy
NumPy(Numerical Python)是Python中最重要的科学计算库之一,也是数据科学和机器学习领域中最常用的库之一。它提供了一个强大的多维数组对象(ndarray)和一系列用于处理这些数组的函数。
①主要特点和功能
-
多维数组对象(ndarray):NumPy 的核心是 ndarray,它是一个高效的多维数组,可以容纳相同类型的元素。这些数组可以是一维、二维或更高维的,并且支持各种数据类型,如整数、浮点数、复数等。
-
矢量化操作:NumPy 提供了广播(broadcasting)和矢量化操作,允许在整个数组上执行运算,而无需编写显式循环。这种方式通常比纯Python代码更加高效。
-
数学函数和操作:NumPy 提供了丰富的数学函数和操作,包括基本的算术运算、三角函数、指数函数、对数函数等,以及各种线性代数、统计学和随机数生成函数。
-
广播(Broadcasting):NumPy 的广播功能使得对不同形状的数组进行运算变得简单和有效,它自动地将较小数组的形状扩展为较大数组的形状,以使运算可以进行。
-
整合外部代码:NumPy 可以与C、C++和Fortran代码进行无缝整合,可以轻松地调用这些语言中的函数,也可以使用NumPy提供的工具将Python代码转换为这些语言的代码。
-
用于文件输入输出的工具:NumPy 提供了用于读取和写入数组数据的工具,可以方便地将数据存储到磁盘或从磁盘读取数据。
-
绘图功能:虽然 NumPy 本身不提供绘图功能,但它与 Matplotlib 等绘图库结合使用,可以方便地进行数据可视化。
② Numpy基础
import numpy as np
'''向量创建'''
np.random.seed(1) #确保每次运行生成的随机数序列是一样的,2的话每次都生产不同序列
a = np.zeros(4) # a = [0. 0. 0. 0.], a shape = (4,), a data type = float64
a = np.random.random_sample(4) # a = [0.1020911 0.71553298 0.42669572 0.12732302], a shape = (4,), a data type = float64
a = np.arange(4.) # a = [0. 1. 2. 3.], a shape = (4,), a data type = float64
a = np.random.rand(4) # a = [0.62697468 0.16812742 0.34532548 0.47263308], a shape = (4,), a data type = float64
a = np.array([5,4,3,2]) # a = [5 4 3 2], a shape = (4,), a data type = int32
a = np.array([5.,4,3,2]) # a = [5. 4. 3. 2.], a shape = (4,), a data type = float64
'''向量索引'''
a=np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
a[2] # 2
a[2].shape # ()
a[-1] # 9
a[2:7:1] # [2 3 4 5 6] 取左不取右
a[2:7:2] # [2 4 6]
a[3:] # [3 4 5 6 7 8 9]
a[:3] # [0 1 2]
a[:] # [0 1 2 3 4 5 6 7 8 9]
'''向量运算'''
a = np.array([1,2,3,4]) # [1 2 3 4]
b = -a # [-1 -2 -3 -4]
b = np.sum(a) # 10
b = np.mean(a) # 2.5
b = a**2 # [ 1 4 9 16]
a = np.array([ 1, 2, 3, 4])
b = np.array([-1,-2, 3, 4])
a+b # [0 0 6 8] 个数相同才能相加
a = np.array([1, 2, 3, 4])
b=5*a # [ 5 10 15 20]
a = np.array([1, 2, 3, 4])
b = np.array([-1, 4, 3, 2])
np.dot(a,b) # 24
np.dot(a,b).shape # ()
np.dot(b,a) # 24
np.dot(b,a).shape # ()
X = np.array([[1],[2],[3],[4]]) # x.shape (4,1) 4行1列矩阵
X[1] # x[1].shape(1,)
w = np.array([2]) # w.shape (1,) 1行标量
c = np.dot(X[1], w) # X[1]=[2] w=[2] c=4 c.shape ()
'''矩阵创建(二维)'''
a = np.zeros((1, 5)) # a = [[0. 0. 0. 0. 0.]] a.shape = (1, 5), 1行5列 0矩阵
a = np.zeros((2, 1)) # a = [[0.]
# [0.]] a.shape = (2, 1) 2行1列 0矩阵
a = np.random.random_sample((1, 1)) # a = [[0.44236513]] a.shape = (1, 1) 返回[0.0, 1.0) 之间的随机浮点数,形状 1行1列随机数矩阵
a = np.array([[5], [4], [3]]) # a = [[5]
# [4]
# [3]] a.shape = (3, 1)
a = np.array([[5],
[4],
[3]]) # 同上
'''矩阵索引'''
a = np.arange(6).reshape(-1, 2) # a = [[0 1] arange生成0-5的整数列
# [2 3] -1 指自动计算行数
# [4 5]] 2 指定列数为2 a.shape: (3, 2)
a = a[2,0] # 4
a = a[2,0].shape # ()
type(a[2,0]) # <class 'numpy.int64'>
a = a[2] # [4,5]
a = a[2].shape # (2,)
type(a[2,0]) # <class 'numpy.ndarray'>
'''矩阵切片'''
a = np.arange(20).reshape(-1, 10) # a = [[ 0 1 2 3 4 5 6 7 8 9]
# [10 11 12 13 14 15 16 17 18 19]]
a = a[0, 2:7:1] # a = [2 3 4 5 6] a.shape=(5,)
a = a[:, 2:7:1] # a = [[2 3 4 5 6]
# [12 13 14 15 16]] a.shape=(2,5)
a = a[:,:] # a = [[ 0 1 2 3 4 5 6 7 8 9]
# [10 11 12 13 14 15 16 17 18 19]] a.shape = (2, 10)
a = a[1,:] # a = [10 11 12 13 14 15 16 17 18 19] a.shape = (10,)
a = a[1] # a = [10 11 12 13 14 15 16 17 18 19] a.shape = (10,)