import pandas as pd
import numpy as np
数据加载
train_data = pd.read_csv("./zhengqi_train.txt",sep='\t')
test_data = pd.read_csv("./zhengqi_test.txt",sep='\t')
test_data
train_data
V0 | V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | ... | V29 | V30 | V31 | V32 | V33 | V34 | V35 | V36 | V37 | target | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.566 | 0.016 | -0.143 | 0.407 | 0.452 | -0.901 | -1.812 | -2.360 | -0.436 | -2.114 | ... | 0.136 | 0.109 | -0.615 | 0.327 | -4.627 | -4.789 | -5.101 | -2.608 | -3.508 | 0.175 |
1 | 0.968 | 0.437 | 0.066 | 0.566 | 0.194 | -0.893 | -1.566 | -2.360 | 0.332 | -2.114 | ... | -0.128 | 0.124 | 0.032 | 0.600 | -0.843 | 0.160 | 0.364 | -0.335 | -0.730 | 0.676 |
2 | 1.013 | 0.568 | 0.235 | 0.370 | 0.112 | -0.797 | -1.367 | -2.360 | 0.396 | -2.114 | ... | -0.009 | 0.361 | 0.277 | -0.116 | -0.843 | 0.160 | 0.364 | 0.765 | -0.589 | 0.633 |
3 | 0.733 | 0.368 | 0.283 | 0.165 | 0.599 | -0.679 | -1.200 | -2.086 | 0.403 | -2.114 | ... | 0.015 | 0.417 | 0.279 | 0.603 | -0.843 | -0.065 | 0.364 | 0.333 | -0.112 | 0.206 |
4 | 0.684 | 0.638 | 0.260 | 0.209 | 0.337 | -0.454 | -1.073 | -2.086 | 0.314 | -2.114 | ... | 0.183 | 1.078 | 0.328 | 0.418 | -0.843 | -0.215 | 0.364 | -0.280 | -0.028 | 0.384 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
2883 | 0.190 | -0.025 | -0.138 | 0.161 | 0.600 | -0.212 | 0.757 | 0.584 | -0.026 | 0.904 | ... | 0.128 | -0.208 | 0.809 | -0.173 | 0.247 | -0.027 | -0.349 | 0.576 | 0.686 | 0.235 |
2884 | 0.507 | 0.557 | 0.296 | 0.183 | 0.530 | -0.237 | 0.749 | 0.584 | 0.537 | 0.904 | ... | 0.291 | -0.287 | 0.465 | -0.310 | 0.763 | 0.498 | -0.349 | -0.615 | -0.380 | 1.042 |
2885 | -0.394 | -0.721 | -0.485 | 0.084 | 0.136 | 0.034 | 0.655 | 0.614 | -0.818 | 0.904 | ... | 0.291 | -0.179 | 0.268 | 0.552 | 0.763 | 0.498 | -0.349 | 0.951 | 0.748 | 0.005 |
2886 | -0.219 | -0.282 | -0.344 | -0.049 | 0.449 | -0.140 | 0.560 | 0.583 | -0.596 | 0.904 | ... | 0.216 | 1.061 | -0.051 | 1.023 | 0.878 | 0.610 | -0.230 | -0.301 | 0.555 | 0.350 |
2887 | 0.368 | 0.380 | -0.225 | -0.049 | 0.379 | 0.092 | 0.550 | 0.551 | 0.244 | 0.904 | ... | 0.047 | 0.057 | -0.042 | 0.847 | 0.534 | -0.009 | -0.190 | -0.567 | 0.388 | 0.417 |
2888 rows × 39 columns
#取所有的特征列
X_train = train_data.iloc[:,:-1]
#最后一列为y
y_train = train_data[['target']] #取目标列要用[[]],表示二维
print("X_train:",X_train.shape,"y_train:",y_train.shape)
X_train: (2888, 38) y_train: (2888, 1)
计算Wx+b用的是矩阵乘法(W,X都为矩阵),所以,要吧b搞进矩阵
方法:在X矩阵多加全为1的一列,在W矩阵多初始化以为W0作为b
# X矩阵多加全为1的一列
X_train['bias'] = 1
test_data['bias'] = 1
X_train
V0 | V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | ... | V29 | V30 | V31 | V32 | V33 | V34 | V35 | V36 | V37 | bias | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.566 | 0.016 | -0.143 | 0.407 | 0.452 | -0.901 | -1.812 | -2.360 | -0.436 | -2.114 | ... | 0.136 | 0.109 | -0.615 | 0.327 | -4.627 | -4.789 | -5.101 | -2.608 | -3.508 | 1 |
1 | 0.968 | 0.437 | 0.066 | 0.566 | 0.194 | -0.893 | -1.566 | -2.360 | 0.332 | -2.114 | ... | -0.128 | 0.124 | 0.032 | 0.600 | -0.843 | 0.160 | 0.364 | -0.335 | -0.730 | 1 |
2 | 1.013 | 0.568 | 0.235 | 0.370 | 0.112 | -0.797 | -1.367 | -2.360 | 0.396 | -2.114 | ... | -0.009 | 0.361 | 0.277 | -0.116 | -0.843 | 0.160 | 0.364 | 0.765 | -0.589 | 1 |
3 | 0.733 | 0.368 | 0.283 | 0.165 | 0.599 | -0.679 | -1.200 | -2.086 | 0.403 | -2.114 | ... | 0.015 | 0.417 | 0.279 | 0.603 | -0.843 | -0.065 | 0.364 | 0.333 | -0.112 | 1 |
4 | 0.684 | 0.638 | 0.260 | 0.209 | 0.337 | -0.454 | -1.073 | -2.086 | 0.314 | -2.114 | ... | 0.183 | 1.078 | 0.328 | 0.418 | -0.843 | -0.215 | 0.364 | -0.280 | -0.028 | 1 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
2883 | 0.190 | -0.025 | -0.138 | 0.161 | 0.600 | -0.212 | 0.757 | 0.584 | -0.026 | 0.904 | ... | 0.128 | -0.208 | 0.809 | -0.173 | 0.247 | -0.027 | -0.349 | 0.576 | 0.686 | 1 |
2884 | 0.507 | 0.557 | 0.296 | 0.183 | 0.530 | -0.237 | 0.749 | 0.584 | 0.537 | 0.904 | ... | 0.291 | -0.287 | 0.465 | -0.310 | 0.763 | 0.498 | -0.349 | -0.615 | -0.380 | 1 |
2885 | -0.394 | -0.721 | -0.485 | 0.084 | 0.136 | 0.034 | 0.655 | 0.614 | -0.818 | 0.904 | ... | 0.291 | -0.179 | 0.268 | 0.552 | 0.763 | 0.498 | -0.349 | 0.951 | 0.748 | 1 |
2886 | -0.219 | -0.282 | -0.344 | -0.049 | 0.449 | -0.140 | 0.560 | 0.583 | -0.596 | 0.904 | ... | 0.216 | 1.061 | -0.051 | 1.023 | 0.878 | 0.610 | -0.230 | -0.301 | 0.555 | 1 |
2887 | 0.368 | 0.380 | -0.225 | -0.049 | 0.379 | 0.092 | 0.550 | 0.551 | 0.244 | 0.904 | ... | 0.047 | 0.057 | -0.042 | 0.847 | 0.534 | -0.009 | -0.190 | -0.567 | 0.388 | 1 |
2888 rows × 39 columns
线性回归+梯度下降
批量梯度下降BGD
损失函数 : J ( θ ) = 1 2 ∑ i = 1 n ( h θ ( x ( i ) ) − y ( i ) ) 2 损失函数:J(\theta) = \frac{1}{2}\sum\limits_{i = 1}^n(h_{\theta}(x^{(i)}) - y^{(i)})^2 损失函数:J(θ)=21i=1∑n(hθ(x(i))−y(i))2
批量梯度下降法是最原始的形式,它是指在每次迭代使用所有样本来进行梯度的更新。每次迭代参数更新公式如下:
$$
梯度更新:\theta_j^{n + 1} = \theta_j^{n} - \eta * (h_{\theta}(x) - y )x_j
矩阵写法:
$$
θ n + 1 = θ n − η ∗ X T ( X θ − y ) \theta^{n + 1} = \theta^{n} - \eta * X^T(X\theta -y) θn+1=θn−η∗XT(Xθ−y)
代码实现
# 先把数据转为np格式
X = X_train.values
y = y_train.values
print(X.shape)
print(y.shape)
#============================定义超参数=====================================
t0 = 5
t1 = 100000
#定义一个函数来调整一下学习率(逆时调整),随着迭代次数的增加,学习率应该变小
def lrate_descent(t):
return t0 / (t + t1) #初始学习率lr = 5 / 100000=0.00005
# 随机初始化W矩阵
W = np.random.randn(X.shape[1],1)
#均方差
mse = 0
last_mse = mse + 1
#迭代次数
times = 0
#停止迭代的精度
accuracy = 0.001
#==========================梯度下降=========================
while True:
if np.abs(mse - last_mse) < accuracy:
break
#loss的梯度
grid = X.T.dot(X.dot(W) - y)
#学习率
lrate = lrate_descent(times)
#梯度更新
W = W - lrate * grid
#计算均方差
last_mse = mse
mse = ((X.dot(W) - y) ** 2).mean()
times += 1
print("迭代第{}次,均方差是:{}".format(times,mse))
b = W[-1]
print("最终的迭代{}次,参数矩阵W:{},bias:{}\n".format(times,W,b))
(2888, 39)
(2888, 1)
迭代第1次,均方差是:8.721127701275023
迭代第2次,均方差是:6.269560464833249
...
最终的迭代147次,参数矩阵W:[[ 0.77104869]
[ 0.12510433]
[-0.07367257]
...
[ 0.32888301]],bias:[0.32888301]
#预测
X_test = test_data.values
y_pred = X_test.dot(W)
np.savetxt('./批量梯度下降.txt',y_pred)
print(y_pred.shape)
(1925, 1)
均方差:score:4.6368
SGD随机梯度下降
随机梯度下降法不同于批量梯度下降,随机梯度下降是每次迭代使用一个样本来对参数进行更新。
:
θ
n
+
1
=
θ
n
−
η
∗
X
T
(
X
θ
−
y
)
\theta^{n + 1} = \theta^{n} - \eta * X^T(X\theta -y)
θn+1=θn−η∗XT(Xθ−y)
批量梯度下降算法每次都会使用全部训练样本,因此这些计算是冗余的,因为每次都使用完全相同的样本集。而随机梯度下降算法每次只随机选择一个样本来更新模型参数,因此每次的学习是非常快速的。
# 先把数据转为np格式
X = X_train.values
y = y_train.values
print(X.shape)
print(y.shape)
#============================定义超参数=====================================
t0 = 5
t1 = 100000
#定义一个函数来调整一下学习率(逆时调整),随着迭代次数的增加,学习率应该变小
def lrate_descent(t):
return t0 / (t + t1) #初始学习率lr = 5 / 100000=0.00005
# 随机初始化W矩阵
W = np.random.randn(X.shape[1],1)
#均方差
mse = 0
last_mse = mse + 1
#迭代次数
times = 0
#停止迭代的精度
accuracy = 0.001
#==========================梯度下降=========================
while True:
if np.abs(mse - last_mse) < accuracy:
break
#=====================打乱数据============================================
index = np.arange(X.shape[0])
#打乱索引值
np.random.shuffle(index)
#numpy的花式索引,把样本打乱给成index的排序
X = X[index]
y = y[index]
#=======================================================================================
#================随机抽取样本来更新梯度=========================================
for i in range(X.shape[0]):
# 抽取一个样本
X_i = X[[i]]
y_i = y[[i]]
#loss的梯度
grid = X_i.T.dot(X_i.dot(W) - y_i)
#学习率
lrate = lrate_descent(times)
#梯度更新
W = W - lrate * grid
#计算均方差
last_mse = mse
mse = ((X.dot(W) - y) ** 2).mean()
times += 1
print("迭代第{}次,均方差是:{}".format(times,mse))
b = W[-1]
print("最终的迭代{}次,参数矩阵W:{},bias:{}\n".format(times,W,b))
(2888, 39)
(2888, 1)
迭代第1次,均方差是:20.908397509630017
...
最终的迭代129次,参数矩阵W:[[ 0.21884206]
[ 0.52453898]
...
[-0.14529283]
[ 0.68916026]],bias:[0.68916026]
预测:
X2 = test_data.values
y_pred = X2.dot(W)
print(y_pred.shape)
np.savetxt('./随机梯度下降.txt',y_pred)
(1925, 1)
均方差:score:4.8463
MBGD小批量梯度下降
小批量梯度下降,是对批量梯度下降以及随机梯度下降的一个折中办法。其思想是:每次迭代使用总样本中的一部分(batch_size)样本来对参数进行更新。这里我们假设 batch_size = 32,样本数 n = 1000 。实现了更新速度与更新次数之间的平衡。每次迭代参数更新公式如下:
θ
j
n
+
1
=
θ
j
n
−
η
∗
1
b
a
t
c
h
_
s
i
z
e
∑
i
=
1
b
a
t
c
h
_
s
i
z
e
(
h
θ
(
x
(
i
)
)
−
y
(
i
)
)
x
j
(
i
)
\theta_j^{n + 1} = \theta_j^{n} - \eta *\frac{1}{batch\_size}\sum\limits_{i = 1}^{batch\_size} (h_{\theta}(x^{(i)}) - y^{(i)} )x_j^{(i)}
θjn+1=θjn−η∗batch_size1i=1∑batch_size(hθ(x(i))−y(i))xj(i)
相对于随机梯度下降算法,小批量梯度下降算法降低了收敛波动性, 即降低了参数更新的方差,使得更新更加稳定。相对于全量梯度下降,其提高了每次学习的速度。并且其不用担心内存瓶颈从而可以利用矩阵运算进行高效计算。
一般情况下,小批量梯度下降是梯度下降的推荐变体,特别是在深度学习中。每次随机选择2的幂数个样本来进行学习,例如:8、16、32、64、128、256。因为计算机的结构就是二进制的。但是也要根据具体问题而选择,实践中可以进行多次试验, 选择一个更新速度与更次次数都较适合的样本数。
在这选择batch_size = 32
# 先把数据转为np格式
X = X_train.values
y = y_train.values
print(X.shape)
print(y.shape)
#总样本
n = X.shape[0]
batch_size = 32
#总batch数
num_batches = int(n / batch_size) # 90
#============================定义超参数=====================================
t0 = 5
t1 = 100000
#定义一个函数来调整一下学习率(逆时调整),随着迭代次数的增加,学习率应该变小
def lrate_descent(t):
return t0 / (t + t1) #初始学习率lr = 5 / 100000=0.00005
# 随机初始化W矩阵
W = np.random.randn(X.shape[1],1)
#均方差
mse = 0
last_mse = mse + 1
#迭代次数
times = 0
#停止迭代的精度
accuracy = 0.001
#==========================梯度下降=========================
while True:
if np.abs(mse - last_mse) < accuracy:
break
#=====================打乱数据============================================
index = np.arange(X.shape[0])
#打乱索引值
np.random.shuffle(index)
#numpy的花式索引,把样本打乱给成index的排序
X = X[index]
y = y[index]
#=======================================================================================
#================按batch数来迭代更新========================================
for i in range(num_batches):
# 抽取一批样本(32个)
X_batch = X[i * batch_size : (i + 1)*batch_size]
y_batch = y[i * batch_size : (i + 1)*batch_size]
#loss的梯度
grid = X_batch.T.dot(X_batch.dot(W) - y_batch)
#学习率
lrate = lrate_descent(times)
#梯度更新
W = W - lrate * grid
#计算均方差
last_mse = mse
mse = ((X.dot(W) - y) ** 2).mean()
times += 1
print("迭代第{}次,均方差是:{}".format(times,mse))
b = W[-1]
print("最终的迭代{}次,参数矩阵W:{},bias:{}\n".format(times,W,b))
(2888, 39)
(2888, 1)
迭代第1次,均方差是:14.126515548387038
...
迭代第121次,均方差是:0.19214031423470773
最终的迭代121次,参数矩阵W:[[ 0.38160319]
[ 0.59695493]
...
[ 0.04837792]],bias:[0.04837792]
# 进行预测
X2 = test_data.values
y_pred = X2.dot(W)
np.savetxt('./小批量梯度下降.txt',y_pred)
score:0.7879
总结:
梯度下降法(Gradient Descent)是一个算法,但不是像多元线性回归那样是一个具体做回归任务的算法,而是一个非常通用的优化算法来帮助一些机器学习算法(都是无约束最优化问题)求解出最优解, 所谓的通用就是很多机器学习算法都是用梯度下降,甚至深度学习也是用它来求解最优解。所有优化算法的目的都是期望以最快的速度把模型参数θ求解出来,梯度下降法就是一种经典常用的优化算法。
三种梯度下降策略的特点总结:
BGD:
优点:
(1)一次迭代是对所有样本进行计算,此时利用矩阵进行操作,实现了并行。
(2)由全数据集确定的方向能够更好地代表样本总体,从而更准确地朝向极值所在的方向。当目标函数为凸函数时,BGD一定能够得到全局最优。
缺点:
(1)当样本数目 n 很大时,每迭代一步都需要对所有样本计算,训练过程会很慢。
从迭代的次数上来看,BGD迭代的次数相对较少。其迭代的收敛曲线示意图可以表示如下:
SGD:
优点:
(1)由于不是在全部训练数据上的更新计算,而是在每轮迭代中,随机选择一条数据进行更新计算,这样每一轮参数的更新速度大大加快。
缺点:
(1)准确度下降。由于即使在目标函数为强凸函数的情况下,SGD仍旧无法做到线性收敛。
(2)可能会收敛到局部最优,由于单个样本并不能代表全体样本的趋势。
为什么SGD收敛速度比BGD要快:
- 我们假设有30W个样本,对于BGD而言,每次迭代需要计算30W个样本才能对参数进行一次更新,需要求得最小值可能需要多次迭代(假设这里是10)。
- 而对于SGD,每次更新参数只需要一个样本,因此若使用这30W个样本进行参数更新,则参数会被迭代30W次,而这期间,SGD就能保证能够收敛到一个合适的最小值上了。
- 也就是说,在收敛时,BGD计算了 10×30W 次,而SGD只计算了 1×30W 次。
从迭代的次数上来看,SGD迭代的次数较多,在解空间的搜索过程就会盲目一些。其迭代的收敛曲线示意图可以表示如下:
MBGD:
相对于随机梯度下降算法,小批量梯度下降算法降低了收敛波动性, 即降低了参数更新的方差,使得更新更加稳定。相对于全量梯度下降,其提高了每次学习的速度。并且其不用担心内存瓶颈从而可以利用矩阵运算进行高效计算。
一般情况下,小批量梯度下降是梯度下降的推荐变体,特别是在深度学习中。每次随机选择2的幂数个样本来进行学习,例如:8、16、32、64、128、256。因为计算机的结构就是二进制的。但是也要根据具体问题而选择,实践中可以进行多次试验, 选择一个更新速度与更次次数都较适合的样本数。
MBGD梯度下降迭代的收敛曲线更加温柔一些:
梯度下降优化
不管用上面三种哪一种,都存在一些挑战与问题,我们可以从以下几点进行优化:
-
选择一个合理的学习速率很难。如果学习速率过小,则会导致收敛速度很慢。如果学习速率过大,那么其会阻碍收敛,即在极值点附近会振荡。
-
学习速率调整,试图在每次更新过程中, 改变学习速率。从经验上看,**学习率在一开始要保持大些来保证收敛速度,在收敛到最优点附近时要小些以避免来回震荡。**比较简单的学习率调整可以通过 **学习率衰减(Learning Rate Decay)**的方式来实现。
假设初始化学习率为 η 0 ,在第 t 次迭代时的学习率 η t 。 假设初始化学习率为 \eta_0,在第 t 次迭代时的学习率 \eta_t。 假设初始化学习率为η0,在第t次迭代时的学习率ηt。
常用的衰减方式为可以设置为 按迭代次数 进行衰减,迭代次数越大,学习率越小!
-
模型所有的参数每次更新都是使用相同的学习速率。如果数据特征是稀疏的,或者每个特征有着不同的统计特征与空间,那么便不能在每次更新中每个参数使用相同的学习速率,那些很少出现的特征应该使用一个相对较大的学习速率。
-
对于非凸目标函数,容易陷入那些次优的局部极值点中,如在神经网路中。那么如何避免呢。
简单的问题,一般使用随机梯度下降即可解决。在深度学习里,对梯度下降进行了很多改进,比如:自适应梯度下降。
-
轮次和批次
轮次:epoch,轮次顾名思义是把我们已有的训练集数据学习多少轮,迭代多少次。
批次:batch,批次这里指的的我们已有的训练集数据比较多的时候,一轮要学习太多数据, 那就把一轮次要学习的数据分成多个批次,一批一批数据的学习。
就好比,你要背诵一片《赤壁赋》,很长。你在背诵的时候,一段段的背诵,就是批次batch。花费了一天终于背诵下来了,以后的9天,每天都进行一轮背诵复习,这就是轮次epoch。这样,《赤壁赋》的背诵效果,就非常牢固了。
在进行,机器学习训练时,我们也要合理选择轮次和批次~