栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

感知器算法及其python 实现 V2.0

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

感知器算法及其python 实现 V2.0

感知器算法及其python 实现2.0

#机器学习/学习算法

先直接上效果图(验证集上)

这是我第二次重新写。之前第一次写的,说实话很多毛病(能跑、能训练,但训练效果和训练速度都很慢,我猜测原因是我 pandas 和 numpy 数据类型混用了,且混得很难看)

相比之前的版本,训练速度快的不是一点半点,保守估计至少快了10倍。(之前那个我一般epoch 设置 10、20、100这样,都很慢。现在设置5000 都和之前 10 时间差不多)当然都是在 cpu 上跑的。

n_sample=4000,epoch=6000,l_rate=0.00001


这是效果最好的一次,准确的达到了100%,错分类次数为0(在验证集上),样本点一共4000个, 下面的两个例子样本点都是1000,可以发现数据集足够大,对模型的拟合是很有帮助的。

epoch=10000,l_rate= 0.00001


可以看出,表现还是很理想的。

epoch=10000,l_rate= 0.000001


可以看出,在只改动学习率(从 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.py
import 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()


# %%





转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/529319.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号