#机器学习/学习算法
先直接上效果图(验证集上)这是我第二次重新写。之前第一次写的,说实话很多毛病(能跑、能训练,但训练效果和训练速度都很慢,我猜测原因是我 pandas 和 numpy 数据类型混用了,且混得很难看)
相比之前的版本,训练速度快的不是一点半点,保守估计至少快了10倍。(之前那个我一般epoch 设置 10、20、100这样,都很慢。现在设置5000 都和之前 10 时间差不多)当然都是在 cpu 上跑的。
n_sample=4000,epoch=6000,l_rate=0.00001
这是效果最好的一次,准确的达到了100%,错分类次数为0(在验证集上),样本点一共4000个, 下面的两个例子样本点都是1000,可以发现数据集足够大,对模型的拟合是很有帮助的。
可以看出,表现还是很理想的。
可以看出,在只改动学习率(从
1
e
−
5
1e^{-5}
1e−5 到
1
e
−
6
1e^{-6}
1e−6),且只是降低的情况,其表现居然睡着训练epoch 的变大而变差,但是它似乎能很快的到达一个低点。而学习率在
1
e
−
5
1e^{-5}
1e−5 的情况下,虽然收敛速度较慢,但最后还是会收敛到一个理想的值,但是它还是会再1000 epoch 左右跳变。
关于这个lost 曲线,以及不收敛的原因,我暂时无法理解。。。有理解的希望能帮忙解答一下,感激不尽。我之后也会问一下老师。
感知器算法理论看这篇即可感知器算法及python实现_Sail的博客-CSDN博客
与之前代码的区别- 没有使用 sklearn 模块,数据集生成(只是半月数据),数据集训练集分离我尝试自己写的,发现其实很多东西没自己想的那么麻烦。这些我打包到了一个 JY_Toolkit.py 包里,需要可以取,当然用 sklearn 的也没问题,我写的时候 在输入参数 和返回数据的类型上是兼容的。
- 参数优化方法有两种,其中一种可以看上一篇博客(这里把上一篇的地址放这:感知器算法及python实现_Sail的博客-CSDN博客),另外一种优化方法如下:
即:
w
n
+
1
=
w
n
+
ϵ
[
y
k
−
y
(
w
T
x
k
)
]
x
k
w_{n+1} = w_n + epsilon[y^k - y(w^Tx^k)]x^k
wn+1=wn+ϵ[yk−y(wTxk)]xk
ϵ
epsilon
ϵ 为学习率,
y
k
,
x
k
y^k,x^k
yk,xk 为理想输出和输入特征向量。
y
(
w
T
x
k
)
y(w^Tx^k)
y(wTxk) 为模型的预测输出。
若预测正确,则
w
n
w_n
wn 不变,若预测错误,则根据
[
y
k
−
y
(
w
T
x
k
)
]
[y^k - y(w^Tx^k)]
[yk−y(wTxk)] 调整参数。以此来逐步修正。(这个我看的 《python machine learning》 21页)
纯手打。
JY_Toolkit.pyimport matplotlib.pyplot as plt
import numpy as np
from random import uniform, seed, shuffle ,sample
import math
import logging
# from random import random
'''JY_Toolkit.py'''
class Jy_makeDataset(object):
def random_state(random_seed):
seed(int(random_seed))
def draw_HalfMoon(n_sample: int = 1000, # 样本点个数,两个分类一共 n_sample
w: float = 1, # 半月的线宽
radius: float = 4, # 半月的半径
hor_distance: float = 4, # Horizontal direction distance for two point
ver_distance: float = 0, # Vertical direction distance for two point
slope: float = 0, # 半月倾斜的角度 [0 ~ 180]
positive_val: int = 1,
negative_val: int = -1,
):
slope %= 180 # make the `slope` between 0 and 180
# 将 n_sample 和样本分为两类每个样本 n_sample / 2 类
each_m = n_sample//2
# circle origin point of positive moon [x , y]
p_origin = [1 + w/2 + radius, 1 + w/2 + radius + ver_distance]
# circle origin point of negative moon [x , y]
n_origin = [p_origin[0] + hor_distance, p_origin[1] - ver_distance]
# product positive point
p_sample = []
n_sample = []
for i in range(each_m):
# Randomly generate l
temp_l = radius + uniform(-(w/2), w/2)
# Randomly generate angle i.e. theta
temp_angle = uniform(slope, slope + 180)
point_x = p_origin[0] + temp_l*math.cos(math.pi/180*temp_angle)
point_y = p_origin[1] + temp_l*math.sin(math.pi/180*temp_angle)
p_sample.append([point_x, point_y, positive_val])
for i in range(each_m):
# Randomly generate l
temp_l = radius + uniform(-(w/2), w/2)
# Randomly generate angle i.e. theta , but the angle of negative point should between `slope + 180` and `slope + 360`
temp_angle = uniform(slope + 180, slope + 360)
point_x = n_origin[0] + temp_l*math.cos(math.pi/180*temp_angle)
point_y = n_origin[1] + temp_l*math.sin(math.pi/180*temp_angle)
n_sample.append([point_x, point_y, negative_val])
sample_points = p_sample + n_sample
shuffle(sample_points)
sample_points = np.array(sample_points)
return sample_points[:, 0:2], sample_points[:, 2]
pass
class Jy_dataSetProcess(object):
def Jy_train_test_split(X,
y,
test_size : 0.2,
):
data = np.column_stack((X,y))
if test_size >= 1 and test_size <= 0:
logging.exception('test_size must be greater than 0 less than 1, we will assign test_size value of 0.2')
test_size = 0.2
sample_count = int(len(data)*test_size)
'''
分离思路:
先将输入的数据集打乱,然后取前 test_size 部分为测试集,后部分为训练集
'''
shuffle(data)
X_test = data[0:sample_count-1]
X_train = data[sample_count:]
return X_train[:,0:2], X_test[:,0:2] ,X_train[:,2] , X_test[:,2]
pass
if __name__ == '__main__':
random_seed = 52
Jy_makeDataset.random_state(random_seed)
np_data, label = Jy_makeDataset.draw_HalfMoon(n_sample=2000)
p_point_x1 = [np_data[i][0] for i in range(len(np_data)) if label[i] == 1]
p_point_x2 = [np_data[i][1] for i in range(len(np_data)) if label[i] == 1]
n_point_x1 = [np_data[i][0] for i in range(len(np_data)) if label[i] == -1]
n_point_x2 = [np_data[i][1] for i in range(len(np_data)) if label[i] == -1]
fig = plt.figure(num="HalfMoons", figsize=(8, 8))
ax1 = fig.add_subplot(111)
ax1.scatter(p_point_x1, p_point_x2, c='red')
ax1.scatter(n_point_x1, n_point_x2, c='blue')
plt.show()
print(np_data)
Perceptron_v2.py
下面代码是直接用 vscode 中 jupyter 插件,从 .ipynb 文件转成的 .py文件。所以这个文件本身是 Perceptron_v2.ipynb
# 要添加一个新单元,输入 '# %%'
# 要添加一个新的标记单元,输入 '# %% [markdown]'
# %%
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from JY_Toolkit import Jy_makeDataset,Jy_dataSetProcess
from sklearn.datasets import make_classification
# I will try to complete the program without the module
# from sklearn.model_selection import train_test_split
random_seed = 5225
# %% [markdown]
# # 生成数据集,并显示数据集分布
#
# JY_makeDataset 是我自己写的一个用于生成数据集的包,不过目前里面只有生成这种两个半圆的功能
# %%
# Set random number seed for random module to make it easy to reproduce
Jy_makeDataset.random_state(random_seed)
## using the Jy_makeDataset.draw_HalfMoon (我自己写的 数据集 生成程序)
X,y = Jy_makeDataset.draw_HalfMoon(n_sample=1000,positive_val= 1,negative_val= -1)
## using the sklearn.datasets.make_classification to generate data set
# X,y = make_classification(n_samples=2000, n_features=2,n_redundant=0,n_informative=1,n_clusters_per_class=1,random_state= random_seed)
y = np.array(list(map(lambda x : 1 if x == 1 else -1,list(y))))
fig = plt.figure(num = 1 , figsize=(8,8))
p_x1 = [X[i,0] for i in range(len(X)) if y[i] == 1]
p_x2 = [X[i,1] for i in range(len(X)) if y[i] == 1]
n_x1 = [X[i,0] for i in range(len(X)) if y[i] == -1]
n_x2 = [X[i,1] for i in range(len(X)) if y[i] == -1]
ax1 = fig.add_subplot(111)
ax1.scatter(p_x1,p_x2,c = 'blue',alpha = 0.4)
ax1.scatter(n_x1,n_x2,c = 'red',alpha = 0.4)
plt.show()
# %% [markdown]
# # 训练集数据集分离
# %%
## 这里先直接使用了 sklearn 只自带的 分离模块
# X_train,X_test,y_train,y_test = train_test_split(X,y,test_size = 0.2,random_state = random_seed)
## 这里用我自己写的
X_train,X_test,y_train,y_test = Jy_dataSetProcess.Jy_train_test_split(X,y,test_size = 0.2)
fig = plt.figure(num = 1 , figsize=(8,8))
p_x1 = [X_test[i,0] for i in range(len(X_test)) if y_test[i] == 1]
p_x2 = [X_test[i,1] for i in range(len(X_test)) if y_test[i] == 1]
n_x1 = [X_test[i,0] for i in range(len(X_test)) if y_test[i] == -1]
n_x2 = [X_test[i,1] for i in range(len(X_test)) if y_test[i] == -1]
ax1 = fig.add_subplot(111)
ax1.scatter(p_x1,p_x2,c = 'blue',alpha = 0.4)
ax1.scatter(n_x1,n_x2,c = 'red',alpha = 0.4)
plt.show()
# %%
class Perceptron(object):
# fit training data to get a Suitable parameters
def __init__(self,
# the dimension of the parameter (dim(x))
w_dim,
epoch=10, # the train times
l_rate=0.01): # learning rate
self.w = np.ones(w_dim + 1, dtype=np.float32)
self.l_rate = l_rate
self.epoch = epoch
self.lost = []
def sign(self, x):
y = float(np.dot(x, self.w))
if y >= 0:
return 1
else:
return -1
def weighted_sum(self,x):
return float(np.dot(x,self.w))
def weighted_sum_all(self,X):
return np.dot(X,self.w)
def fit(self,
# input features set -- type should be `numpy.ndarray` (best)
X_train,
y_train): # Expected label/output
temp = np.ones(len(X_train))
X_train = np.column_stack((X_train, temp)) # up X dimension
del temp # Recycle temp
self.lost.clear() # claer the list of lost
lost_temp = 0
for iter_ in range(self.epoch):
for i in range(len(X_train)):
x = X_train[i]
y = y_train[i]
# SGD
fit_res = self.sign(x)
if y != fit_res:
lost_temp += 1
## solution 1 :
# self.w = self.w + self.l_rate*(y - fit_res)*x
## solution 2 :
self.w += x*y*self.l_rate
self.lost.append(lost_temp)
if iter_%5 == 0:
print('iter_:',iter_,'lost:',lost_temp)
lost_temp = 0
def predict(self,X_test):
temp = np.ones(len(X_test))
X_test = np.column_stack((X_test, temp)) # up X dimension
del temp # Recycle temp
y_predict = list(map(lambda x : 1 if x >= 0 else -1,list(self.weighted_sum_all(X_test))))
return np.array(y_predict)
def score(self,y,label):
accuracy = 0
for i in range(len(y)):
if y[i] == label[i]:
accuracy += 1
return accuracy / len(label), len(label) - accuracy
pass
# %%
if __name__ == '__main__':
perceptron = Perceptron(X_train.shape[1],epoch=8000,l_rate= 0.00001)
perceptron.fit(X_train, y_train)
y_predict = perceptron.predict(X_test)
accuracy_rate, lost_count = perceptron.score(y_test, y_predict)
print('score:',accuracy_rate,'test lost:',lost_count,'last lost:',perceptron.lost[len(perceptron.lost)-1])
# %%
if __name__ == '__main__':
# plt
positive_f1 = [X_test[i][0] for i in range(len(X_test)) if y_test[i] == 1]
positive_f2 = [X_test[i][1] for i in range(len(X_test)) if y_test[i] == 1]
negetive_f1 = [X_test[i][0] for i in range(len(X_test)) if y_test[i] == -1]
negetive_f2 = [X_test[i][1] for i in range(len(X_test)) if y_test[i] == -1]
mistake_f1_pre = [X_test[i][0] for i in range(len(X_test)) if y_predict[i] != y_test[i]]
mistake_f2_pre = [X_test[i][1] for i in range(len(X_test)) if y_predict[i] != y_test[i]]
fig = plt.figure(num=1,figsize=(33,10))
ax1 = fig.add_subplot(131)
ax1.scatter(positive_f1,positive_f2,c = 'red',alpha=0.5)
ax1.scatter(negetive_f1,negetive_f2,c = 'blue',alpha=0.5)
line_x = np.linspace(0,14,100)
# line_x = np.linspace(-3,3,100)
line_w = -1*(perceptron.w[0]/perceptron.w[1])
line_b = -1*(float(perceptron.w[2])/perceptron.w[1])
line_y = list(map(lambda x : x*line_w + line_b ,line_x))
ax1.plot(line_x,line_y,c = 'orange')
ax2 = fig.add_subplot(132)
ax2.scatter(mistake_f1_pre,mistake_f2_pre,c = 'orange',alpha=0.5)
ax2.plot(line_x,line_y,c = 'orange')
ax3 = fig.add_subplot(133)
ax3.plot(range(len(perceptron.lost)),perceptron.lost)
print(line_w,line_b)
plt.show()
# %%



