5.3 隨機(jī)梯度下降法
我們首先來看隨機(jī)梯度下降法的運(yùn)算順序糖耸。前面我們已經(jīng)講了這種算法的學(xué)習(xí)邏輯。它對(duì)整個(gè)數(shù)據(jù)集要循環(huán)好幾次丘薛。每次計(jì)算都利用上一條的數(shù)據(jù)得到的最優(yōu)權(quán)重作為基準(zhǔn)嘉竟,在此基礎(chǔ)上再使用梯度下降來得到新的最優(yōu)權(quán)重。所以對(duì)于我們上面考慮的成本和售價(jià)問題洋侨,就可以分段寫為如下的幾個(gè)部分舍扰。甚至可以建幾個(gè)py文件,分別對(duì)應(yīng)不同的功能希坚,最后用一個(gè)main函數(shù)把它們都串在一起边苹。這樣操作的話,代碼會(huì)比較干凈裁僧。
第一步是數(shù)據(jù)處理个束,正如我們以前強(qiáng)調(diào)的,最好要把數(shù)據(jù)處理成正規(guī)化數(shù)據(jù)锅知,處理代碼如下:
import numpy as np
cost_of_material = np.array([38.3, 35, 31.2, 43.2, 44.2, 41.2])
cost_of_sales = np.array([12.3, 11.2, 10.1, 9.2, 9.1, 9.6])
cost_of_human = np.array([22,21.3,23.7,23.2,20.5,24.2])
cost_of_product = np.array([7.2, 7.8, 8.3, 8.6, 8.8, 9.3])
sell_price = np.array([100.1, 102, 99.2, 101.2, 103.8, 100.5])
# 數(shù)據(jù)正規(guī)化播急,由于都是錢計(jì)價(jià)的,所以可以用同一個(gè)單位來正規(guī)化售睹,比如這里使用100, 也可以不正規(guī)化桩警,這里使用是為了提示你必須先考慮特征的正規(guī)化。
cost_of_material /= 100
cost_of_sales /= 100
cost_of_human /= 100
cost_of_product /= 100
sell_price /= 100
# 學(xué)習(xí)速率
learning_rate = np.array([0.5,0.5,0.5,0.5])
第二步是將隨機(jī)梯度下降的算法部分全部實(shí)現(xiàn)為一個(gè)rand_grad類昌妹。它可以寫成如下的形式:
class rand_grad:
def __init__(self,inputs,outputs,weights):
self.inputs = inputs
self.weights = weights
self.outputs = outputs
# 預(yù)測(cè)函數(shù)
def neural_network(self):
prediction = self.inputs.dot(self.weights)
return prediction
# 代價(jià)函數(shù)
def error_function(self):
prediction = self.neural_network()
return pow(prediction - self.outputs,2)
# 梯度下降乘子
def grad_descent_multiplier(self):
multiplier = (self.neural_network() - \
self.outputs)*inputs
return multiplier
# 運(yùn)算停止控制條件
def stop_condition(self,times):
if (self.error_function()>1e-12 or times<50):
return True
else:
return False
第三步是權(quán)重和數(shù)據(jù)的矩陣化預(yù)處理捶枢,方便使用矩陣的切片以及矩陣乘法握截,我們?cè)谏厦娴念愔袑?shí)現(xiàn)的也是矩陣算法,所以調(diào)用矩陣型的數(shù)據(jù)是比較方便的烂叔。
# 預(yù)設(shè)權(quán)重
weights = np.array([0.6,0.2,0.8,0.9])
# 轉(zhuǎn)換成矩陣形式
raw_data = np.vstack((cost_of_material,cost_of_sales,cost_of_human,cost_of_product))
raw_data = raw_data.T
print(raw_data[0])
第四步是使用循環(huán)嵌套來實(shí)現(xiàn)隨機(jī)梯度下降谨胞,這種算法基本沒有使用矩陣計(jì)算的方便性,所以使用循環(huán)嵌套是隨機(jī)梯度下降的一個(gè)非常重要的特征蒜鸡。在批處理梯度下降中胯努,就把循環(huán)嵌套改寫成了矩陣算法,運(yùn)算效率大大提高逢防。
# 當(dāng)誤差小于10的-10次方叶沛,即(1e-10),即停止運(yùn)算忘朝。
for ergo in range(30): #遍歷30次數(shù)據(jù)集
error_list = []
for item_index in range(len(raw_data)):
# 從第一條數(shù)據(jù)條開始循環(huán)
inputs = raw_data[item_index]
outputs = sell_price[item_index]
weights = weights
engine = rand_grad(inputs,outputs,weights)
第五步是嵌套上每一條數(shù)據(jù)的梯度下降循環(huán)灰署,即:
times = 0
while engine.stop_condition(times):
# 此處為梯度下降
times += 1
gdm = list(engine.grad_descent_multiplier())
if min(np.abs(gdm)) > max(np.abs(weights)):
gdm /= 10*min(np.abs(gdm))/max(np.abs(weights))
# 下降因子相對(duì)于權(quán)重過大,此時(shí)應(yīng)將它縮小局嘁,否則極易引起誤差發(fā)散溉箕。
factor = np.diag(gdm)
weights -= np.matmul(learning_rate,factor)
# 將誤差記錄下來,放到誤差列表中
error_list.append(engine.error_function())
我們可以跟隨每一次循環(huán)數(shù)據(jù)集悦昵,看最終的權(quán)重以及整體誤差變化的情形肴茄。代碼如下:
print("權(quán)重變化過程:", weights)
print("整體誤差變化過程:", sum(error_list))
綜合所有的步驟,整體代碼為:
import numpy as np
cost_of_material = np.array([38.3, 35, 31.2, 43.2, 44.2, 41.2])
cost_of_sales = np.array([12.3, 11.2, 10.1, 9.2, 9.1, 9.6])
cost_of_human = np.array([22,21.3,23.7,23.2,20.5,24.2])
cost_of_product = np.array([7.2, 7.8, 8.3, 8.6, 8.8, 9.3])
sell_price = np.array([100.1, 102, 99.2, 101.2, 103.8, 100.5])
# 數(shù)據(jù)正規(guī)化旱捧,由于都是錢計(jì)價(jià)的独郎,所以可以用同一個(gè)單位來正規(guī)化,比如這里使用100, 也可以不正規(guī)化枚赡,這里使用是為了提示你必須先考慮特征的正規(guī)化。
cost_of_material /= 100
cost_of_sales /= 100
cost_of_human /= 100
cost_of_product /= 100
sell_price /= 100
learning_rate = np.array([0.5,0.5,0.5,0.5])
class rand_grad:
def __init__(self,inputs,outputs,weights):
self.inputs = inputs
self.weights = weights
self.outputs = outputs
# 預(yù)測(cè)函數(shù)
def neural_network(self):
prediction = self.inputs.dot(self.weights)
return prediction
# 代價(jià)函數(shù)
def error_function(self):
prediction = self.neural_network()
return pow(prediction - self.outputs,2)
# 梯度下降乘子
def grad_descent_multiplier(self):
multiplier = (self.neural_network() - \
self.outputs)*inputs
return multiplier
# 運(yùn)算停止控制條件
def stop_condition(self,times):
if (self.error_function()>1e-10 or times<50):
return True
else:
return False
# 預(yù)設(shè)權(quán)重
weights = np.array([0.6,0.2,0.8,0.9])
# 轉(zhuǎn)換成矩陣形式
raw_data = np.vstack((cost_of_material,cost_of_sales,cost_of_human,cost_of_product))
raw_data = raw_data.T
print(raw_data[0])
# 當(dāng)誤差小于10的-10次方谓谦,即(1e-10)贫橙,即停止運(yùn)算。
for ergo in range(30): #遍歷30次數(shù)據(jù)集
error_list = []
for item_index in range(len(raw_data)):
# 從第一條數(shù)據(jù)條開始循環(huán)
inputs = raw_data[item_index]
outputs = sell_price[item_index]
weights = weights
engine = rand_grad(inputs,outputs,weights)
times = 0
while engine.stop_condition(times):
# 此處為梯度下降
times += 1
gdm = list(engine.grad_descent_multiplier())
if min(np.abs(gdm)) > max(np.abs(weights)):
gdm /= 10*min(np.abs(gdm))/max(np.abs(weights))
# 下降因子相對(duì)于權(quán)重過大反粥,此時(shí)應(yīng)將它縮小卢肃,否則極易引起誤差發(fā)散。
factor = np.diag(gdm)
weights -= np.matmul(learning_rate,factor)
error_list.append(engine.error_function())
print("權(quán)重變化過程:", weights)
print("整體誤差變化過程:", sum(error_list))