參考代碼:
https://blog.csdn.net/zy1337602899/article/details/84777396
初次寫機器學習的代碼,大部分參照了鏈接文章中的代碼,稍有改動
源代碼
// An highlighted block
import matplotlib.pyplot as plt
import numpy as np
import scipy.optimize as opt
from sklearn.metrics import classification_report
import pandas as pd
from sklearn import linear_model
#獲取原始數據
def raw_data(path):
data = pd.read_csv(path, names=['exam1','exam2','admit'])
return data
#繪製原始數據
def draw_data(data):
accept = data[data['admit'] == 1]
refuse = data[data['admit'] == 0]
plt.scatter(accept['exam1'], accept['exam2'], c='g', label='admit')
plt.scatter(refuse['exam1'], refuse['exam2'], c='r', label='refuse')
plt.title('admission')
plt.xlabel('exam1')
plt.ylabel('exam2')
return plt
#sigmoid函數
def sigmoid(z):
return 1/(1 + np.exp(-z))
#代價函數
def cost_function(theta,x,y):
m=x.shape[0]
#print(theta_T.shape)
first = -y.dot(np.log(sigmoid(x.dot(theta))))
second = (1 -y).dot(np.log(1 - sigmoid(x.dot(theta))))
#dot運算的過程中已經做了sum工作
#因爲有部分運算是內積 a1b1+a2b2+...+ anbn
return (first - second)/m
def gradient_descent(theta,x,y):
m=x.shape[0]
b = sigmoid(x.dot(theta))
#print(b)
return (b - y).dot(x)
def predict(theta,x):
h = sigmoid(x.dot(theta.T))
return[1 if x >= 0.5 else 0 for x in h]
#決策邊界
def boundary(theta,data):
x1 = np.arange(20,100,0.01)
x2 = -(theta[0] + theta[1]*x1)/theta[2]
plt = draw_data(data)
plt.title("boundary")
plt.plot(x1,x2)
plt.show()
pass
def main():
data=raw_data('F:\jupyterProject\Logistic Regression Exercise\ex2data1.txt')
# print(data.head())
# plt=draw_data(data)
# plt.show()
x1=data['exam1']
x2=data['exam2']
x=np.c_[np.ones(x1.shape[0]),x1,x2]
y=data['admit']
# 不要將theta初始化爲1,初始化爲1的話,h(x)會過大,sigmoid近似返回1,log(1-h(x))無意義
theta=np.zeros(x.shape[1])
times = 200000
learningRate = 0.12
#print(theta[0].shape)
cost_data=np.ones(20001)
#print(gradient_descent(theta,x,y))
#minimize是已封裝好的計算 fun 最小值的方法
#theta_right=opt.minimize(fun=cost_function,x0=theta,args=(x,y),method='tnc',jac=gradient_descent)
#循環20萬次
for i in range(times):
#cost_data[i] = cost_function(theta,x,y)
a = gradient_descent(theta,x,y)
theta= theta - learningRate * a
#print(a)
print(theta)
boundary(theta,data)
#再循環20萬次,對比效果
for i in range(times):
#cost_data[i] = cost_function(theta,x,y)
a = gradient_descent(theta,x,y)
theta= theta - learningRate * a
#print(a)
print(theta)
boundary(theta,data)
#plt.scatter(range(1000),cost_data[:1000], c='k', label='boundary')
main()
效果對比
1.使用 scipy.optimize.minimize方法計算使得costfunction最小的theta,得到如下邊界:
theta:[-25.14325329 0.20608863 0.2013236 ]
圖片來源:https://blog.csdn.net/zy1337602899/article/details/84777396
2.自己根據課程中老師講到的repeat部分編寫的代碼進行的計算(20萬次迭代):
theta:[-73747.32582717 617.25196212 602.09900877]
3.迭代40萬次:
theta:[-66277.53154909 571.89400675 561.94143501]
可以看到相比20萬次迭代,直觀上有了更好的劃分效果
踩的坑 && 總結
1.不熟悉numpy中的dot運算,一度以爲cost_function和gradient_descent中需要(實際上不需要sum):
return sum(first - second)/m
return sum((b - y).dot(x))
輸出部分數據的結構幫助我們理解dot的運算過程
#gradien_descent部分的運算
q = (sigmoid(x.dot(theta))-y)
w = q.dot(x)
print(q.shape)
print(x.shape)
print(w.shape)
output:
(100,)
(100, 3)
(3,)
x是 100×3的矩陣,theta 是個一維行向量,其shape爲(3,),我們可以認爲 x.dot(theta) 運算時自動把theta變成了 3×1的矩陣,這樣得到一個 100×1的矩陣,但是theta實際上不是矩陣,所以得到的結果也不是一個矩陣,而是一個有100個數值的一維行向量,即q。
q是一個一維行向量,x 是100×3的矩陣,這裏認爲dot運算時把q看作1×100的矩陣,得到一個一維行向量(1×3),當然最後的輸出是(3,)格式的數據,這3個數值分別是3個參數的梯度值。由於嫌編輯公式麻煩,具體的由公式推算出代碼的過程就附上一份手寫的吧。
2.關於迭代更新theta
原博中給出的方法是利用已有的scipy.optimize.minimize方法,自己寫好損失函數和梯度函數,作爲參數傳進去,直接獲得最優的theta值,但是爲了體驗一波佛系調參的過程,我嘗試着自己寫theta的更新代碼,關鍵的代碼也就是手寫部分的最後一行,一直重複這一過程就行。最初我一直將迭代次數設置在1萬至5萬,總是出不來好的效果,決策邊界偏得離譜,也不停的修改學習率,都是白搭。後來我索性將迭代次數改爲了20萬次,沒想到這次直接出來了比較好的效果,嗯,果然是佛系。
3.尚未解決的問題
我嘗試畫出cost_function的變化曲線
for i in range(times):
#每迭代100次記錄一次損失函數值
if(i % 100 == 0):
cost_data[int(i/100)] = cost_function(theta,x,y)
a = gradient_descent(theta,x,y)
theta= theta - learningRate * a
#print(a)
plt.scatter(range(10000),cost_data[:10000], c='k', label='boundary')
但是得到的結果卻是這樣的:
很悲傷。
不過程序給報了個warring :RuntimeWarning: divide by zero encountered in log
那麼是不是 sigmoid(x.dot(theta))的結果太小,導致log()的參數趨近於零了呢,這個問題等我研究明白了再回來填坑,或者有大神知道怎麼回事的話還請帶一帶本小白。