2023 華樹杯モデリングのアイデア - ケース: パーセプトロン原理の分析と実装

# コンテストの質問のアイデア

(コンテストの質問が出たらすぐに CSDN で共有します)

https://blog.csdn.net/dc_sinor?type=blog

1. パーセプトロンの直感的な理解

パーセプトロンは機械学習アルゴリズムの中で最も単純なアルゴリズムの 1 つであり、その原理は次の図に示されています。

ここに画像の説明を挿入

たとえば、座標軸 (図の黒線)、水平 x1 軸、垂直 x2 軸があります。グラフ内のすべての点は (x1, x2) によって決定されます。この図を部品の合格判定に当てはめると、x1 が部品の長さ、x2 が部品の品質、座標軸が部品の平均長さと平均重量となり、青いものが合格となります。黄色のものは不良品なので排除する必要があります。部品の長さと重量が平均よりも大きい場合、その部品が適格であることを意味することは明らかです。つまり、第 1 象限内のすべての青い点です。逆に、両方の項目が平均値より小さい場合は、第 3 象限の黄色の点のように劣っています。

予測は非常に簡単で、新しい部品を入手したら、長さ x1 と質量 x2 を測定し、両方の項目が平均値より大きければその部品は合格となります。これが私たち人間の人工知能です。

では、プログラムはどのようにして、長さと重量が平均よりも大きい部品が適格であることを知るのでしょうか?
また

どうやってこのルールを学習したのでしょうか?
プログラムが取得するのは、現在のグラフ内のすべての点の情報とラベルです。つまり、すべてのサンプル x の座標が (x1, x2) であり、それが青または黄色に属することがわかっています。手元にある点について、それらを区切る直線を見つけて新しい部品を入手し、その質量と重量がわかれば、線のどちら側にあるのかがわかるといいのですが、それが良い部分にも悪い部分にも属する可能性があることを知ることができます。たとえば、図内の黄色、青、ピンクの線は、2 つの現在の状況を完全に分離できます。x1 座標軸または x2 座標軸も分割線になる可能性があります (どちらの線もすべての点を正しく分離できます)。

読者の皆さんも、図の 2 つの点の山について、それらを分割できる直線が無数にあることを見たことがあるでしょう。実際、現在の点を分割できる必要があるだけでなく、新しい点が入ってきたときに、次のことを行う必要があります。分かれているのでどのラインが良いでしょうか?

直線はどのようにして最良の分割線に属するのでしょうか? 実際、パーセプトロンは最適な直線を見つけることはできませんが、すべての点を分離できれば、絵に描かれたすべての線を見つけることができます。

結論を導き出します:
直線が点を見逃すことがなければ、それは良い直線です

すべての誤分類された点と直線の間の距離を合計し、この合計を最小の例 (できれば 0、誤分類された点がないことを意味します) にすると、この直線が探しているものになります。

第二に、パーセプトロンの数学的観点

まず、最終的な目標を決めましょう: 最適な分割線を見つける途中の面倒な手順に関係なく、とにかく、最終的に関数 f(x) が生成されます。新しいデータ x を関数に投入すると、関数 f(x) が生成されます。これは青か黄色かを予測して教えてください。それはとても簡単です。だから途中過程は考えず、まずは結果を決めましょう。

ここに画像の説明を挿入

ほら、f(x) が出てきませんでした、符号って何ですか?wx+bとは何ですか?心配しないで、sign 関数とは何かを見てみましょう。

ここに画像の説明を挿入

Sign は非常に単純なようで、x が 0 以上の場合、sign は 1 を出力し、それ以外の場合は -1 を出力します。次に、前方再帰します。wx+b が 0 以上の場合、f(x) は 1 に等しく、それ以外の場合、f(x) は -1 に等しくなります。

では、wx+bとは何でしょうか?
最適な直線です。この式を 2 次元で見てみると、2 次元での直線は y=ax+b と定義されます。2 次元では、w は単に a、b、または b です。つまり、wx+b は直線になります (この記事の冒頭の図の青い線など)。新しい点 x が青い線の左側にある場合、wx+b<0、次に符号の後、最後に f は -1 を出力し、右側にある場合は 1 を出力します。ちょっと無理があるような気がしますが、この状況を 2 次元平面 y=ax+b と等価にして、点が x 軸上にあれば、点線の左右に関係なく、最終的には結果は 0 より大きくなります。これは line と何の関係がありますか? えっと…実はwx+bとax+bは同じ直線の形を表しますが、若干の違いがあります。上の画像を反時計回りに45度回転させてみると、青い線がX軸になるでしょうか?ははは、元の青い線の右側が X 軸の上になったということですか? 実は、パーセプトロンが直線 wx+b を計算するとき、すでに秘密裏に変換されているので、割り算に使用した直線が x 軸となり、左側と右側がそれぞれ x 軸の上と下になります。それはポジティブにもネガティブにもなります。

では、なぜ ax+b ではなく wx+b なのでしょうか?
この記事では部品を例に挙げていますが、部品の性質を表すために上記の長さと重さ (x1、x2) を使用しているため、2 次元の平面で十分です。も関連していますか?次に、色を表す x3 を追加する必要があり、サンプルの属性は (x1, x2, x3) となり、3 次元になります。wx+b は 2 次元の場合にのみ使用されるのではなく、3 次元の場合でもこの式を使用できます。したがって、wx+b と ax+b は 2 次元ではほぼ同じであるだけですが、実際には別のものです。3D の wx+b とは何ですか? 部屋の一方の隅に青い点があり、もう一方の隅に黄色の点があると想像すると、明らかに直線を使用するだけでは十分ではなく、平面が必要です。つまり、3 次元では、wx+b は平面です。その理由については、後ほど詳しく説明します。四次元はどうでしょうか?うーん…四次元空間を分けるものは何なのか説明のしようがないようですが、四次元の場合はナイフのように四次元空間を真っ二つにする何かがあるはずです。半分に切断するには、3 次元の場合は 2 次元の平面、2 次元の場合は 1 次元の平面であるのと同じように、4 次元の場合は平面である必要があります。つまり、4次元におけるwx+bを4次元に対して平面的な物体として表現し、4次元空間を2等分したものを超平面と呼びます。このことから、高次元空間では wx+b が分割超平面となり、これが正式名称となります。

正式に言えば:
wx+b は n 次元空間の超平面 S であり、w は超平面の法線ベクトル、b は超平面の切片です。この超平面は特徴空間を 2 つの部分に分割し、その 2 つの部分に配置されます。点はそれぞれ正と負のカテゴリに分類されるため、超平面 S は分離超平面と呼ばれます。

詳細:

w は超平面の法線ベクトルです。これは w が平面に対して定義される方法です。これは数学的な知識です。Google で学習できます。

b は超平面の切片です。2 次元では ax+b として理解できます。

特徴空間: n 次元空間全体。サンプルの各属性は特徴と呼ばれます。特徴空間とは、サンプルのすべての属性の組み合わせがこの空間で見つかることを意味します。

ここに画像の説明を挿入
f(x) の最初の要件から、1 と -1 のみを出力できるsign(x) に拡張しました。そして今では、wx+b となり、最適な wx を見つけることができる限り、ますます単純に見えます。 +b、パーセプトロンの構築を完了できます。前述したように、誤って分類された点の距離の合計を最大化して超平面を見つけるには、まず点と超平面の間の距離を計算する式を公開し、すべての点の距離式を計算できるようにする必要があります。

ここに画像の説明を挿入

まず、wx+b を見てください。2 次元空間では直線と考えることができます。同時に、変形により、wx+b は画像全体を回転させた後の x 軸になります。すべての点から x 軸までの距離は実際には wx+ です b の値は正しいですか? もちろん、x 軸の下の点を考慮すると、絶対値 -> |wx+b| を加算して、誤って分類されたすべての点の距離の合計を求める、つまり |wx+b の合計を求める必要があります。 | それを最小限に抑えます。とても簡単で、w と b を同じ割合で減らすだけです。たとえば、w を 0.5w に、b を 0.5b に変更すると、線は同じままですが、値が 2 倍に減ります。まだ満足していませんか?縮み続けられるよ!0に縮めろ!したがって、いくつかの制約を追加する必要があります。式全体を w の係数の長さで割ります。どういう意味ですか?つまり、何があっても、w をその単位長さで割る必要があります。w と b を比例して縮小すると、||w|| も比例して縮小し、値は動かず、非常に安定したままになります。法長で割る前の |wx+b| を関数区間と呼び、法長で割った後の |wx+b| を幾何区間と呼び、幾何区間は物理的な意味での実際の長さとみなすことができます。ズームインまたはズームアウトすると、物理的な距離はそのようなものになります。これは不可能です。数値を変更するだけです。機械学習で距離を計算する場合、通常は幾何学的間隔が使用され、そうでないと解が得られません。

ここに画像の説明を挿入
実際には青に属するはずの点 (線の右側、y>0) が、実際には左側 (wx+b<0) にあると予測されるなど、誤って分類されたデータの場合、それは間違っています。結果が負の場合、この時点で符号を追加すると、結果は正となり、w の法長で割ります。これは、超平面に対して誤って分類された単一の点の例です。たとえば、合計は、誤って分類されたすべてのポイントの合計です。

上の図の最後に、法の長さで割ることは考慮されておらず、関数区間になると書かれていますが、なぜこのようなことができるのでしょうか?WBの縮小は考えなかったのですか?私が上で言ったことは間違っていますか?

1 つの説明は次のとおりです: パーセプトロンは誤分類によって駆動されるアルゴリズムであり、その最終的な目標は誤分類点がない場合、合計距離は 0 になり、w と b の値は何の関係もありませんそれと一緒に使ってください。したがって、パーセプトロンの適用においては幾何学的区間と関数区間に違いはなく、計算を簡略化するために関数区間を使用する。

ここに画像の説明を挿入
上記は損失関数の正式な定義です. 除算超平面を求める最終的な目標は損失関数を最小化することです. 0 であれば完全です.
ここに画像の説明を挿入

パーセプトロンは勾配降下法を使用して w と b の最適解を取得し、分割超平面 wx+b を取得します。勾配降下法とそのステップ サイズは空間によって制限されます。自分で Google で調べてください。

3. コードの実装

#coding=utf-8
#Author:Dodo
#Date:2018-11-15
#Email:[email protected]
'''
数据集:Mnist
训练集数量:60000
测试集数量:10000
------------------------------
运行结果:
正确率:81.72%(二分类)
运行时长:78.6s
'''
import numpy as np
import time
def loadData(fileName):
    '''
    加载Mnist数据集
    :param fileName:要加载的数据集路径
    :return: list形式的数据集及标记
    '''
    print('start to read data')
    # 存放数据及标记的list
    dataArr = []; labelArr = []
    # 打开文件
    fr = open(fileName, 'r')
    # 将文件按行读取
    for line in fr.readlines():
        # 对每一行数据按切割福','进行切割,返回字段列表
        curLine = line.strip().split(',')
        # Mnsit有0-9是个标记,由于是二分类任务,所以将>=5的作为1,<5为-1
        if int(curLine[0]) >= 5:
            labelArr.append(1)
        else:
            labelArr.append(-1)
        #存放标记
        #[int(num) for num in curLine[1:]] -> 遍历每一行中除了以第一哥元素(标记)外将所有元素转换成int类型
        #[int(num)/255 for num in curLine[1:]] -> 将所有数据除255归一化(非必须步骤,可以不归一化)
        dataArr.append([int(num)/255 for num in curLine[1:]])
    #返回data和label
    return dataArr, labelArr
def perceptron(dataArr, labelArr, iter=50):
    '''
    感知器训练过程
    :param dataArr:训练集的数据 (list)
    :param labelArr: 训练集的标签(list)
    :param iter: 迭代次数,默认50
    :return: 训练好的w和b
    '''
    print('start to trans')
    #将数据转换成矩阵形式(在机器学习中因为通常都是向量的运算,转换称矩阵形式方便运算)
    #转换后的数据中每一个样本的向量都是横向的
    dataMat = np.mat(dataArr)
    #将标签转换成矩阵,之后转置(.T为转置)。
    #转置是因为在运算中需要单独取label中的某一个元素,如果是1xN的矩阵的话,无法用label[i]的方式读取
    #对于只有1xN的label可以不转换成矩阵,直接label[i]即可,这里转换是为了格式上的统一
    labelMat = np.mat(labelArr).T
    #获取数据矩阵的大小,为m*n
    m, n = np.shape(dataMat)
    #创建初始权重w,初始值全为0。
    #np.shape(dataMat)的返回值为m,n -> np.shape(dataMat)[1])的值即为n,与
    #样本长度保持一致
    w = np.zeros((1, np.shape(dataMat)[1]))
    #初始化偏置b为0
    b = 0
    #初始化步长,也就是梯度下降过程中的n,控制梯度下降速率
    h = 0.0001
    #进行iter次迭代计算
    for k in range(iter):
        #对于每一个样本进行梯度下降
        #李航书中在2.3.1开头部分使用的梯度下降,是全部样本都算一遍以后,统一
        #进行一次梯度下降
        #在2.3.1的后半部分可以看到(例如公式2.6 2.7),求和符号没有了,此时用
        #的是随机梯度下降,即计算一个样本就针对该样本进行一次梯度下降。
        #两者的差异各有千秋,但较为常用的是随机梯度下降。
        for i in range(m):
            #获取当前样本的向量
            xi = dataMat[i]
            #获取当前样本所对应的标签
            yi = labelMat[i]
            #判断是否是误分类样本
            #误分类样本特诊为: -yi(w*xi+b)>=0,详细可参考书中2.2.2小节
            #在书的公式中写的是>0,实际上如果=0,说明改点在超平面上,也是不正确的
            if -1 * yi * (w * xi.T + b) >= 0:
                #对于误分类样本,进行梯度下降,更新w和b
                w = w + h *  yi * xi
                b = b + h * yi
        #打印训练进度
        print('Round %d:%d training' % (k, iter))
    #返回训练完的w、b
    return w, b
def test(dataArr, labelArr, w, b):
    '''
    测试准确率
    :param dataArr:测试集
    :param labelArr: 测试集标签
    :param w: 训练获得的权重w
    :param b: 训练获得的偏置b
    :return: 正确率
    '''
    print('start to test')
    #将数据集转换为矩阵形式方便运算
    dataMat = np.mat(dataArr)
    #将label转换为矩阵并转置,详细信息参考上文perceptron中
    #对于这部分的解说
    labelMat = np.mat(labelArr).T
    #获取测试数据集矩阵的大小
    m, n = np.shape(dataMat)
    #错误样本数计数
    errorCnt = 0
    #遍历所有测试样本
    for i in range(m):
        #获得单个样本向量
        xi = dataMat[i]
        #获得该样本标记
        yi = labelMat[i]
        #获得运算结果
        result = -1 * yi * (w * xi.T + b)
        #如果-yi(w*xi+b)>=0,说明该样本被误分类,错误样本数加一
        if result >= 0: errorCnt += 1
    #正确率 = 1 - (样本分类错误数 / 样本总数)
    accruRate = 1 - (errorCnt / m)
    #返回正确率
    return accruRate
if __name__ == '__main__':
    #获取当前时间
    #在文末同样获取当前时间,两时间差即为程序运行时间
    start = time.time()
    #获取训练集及标签
    trainData, trainLabel = loadData('../Mnist/mnist_train.csv')
    #获取测试集及标签
    testData, testLabel = loadData('../Mnist/mnist_test.csv')
    #训练获得权重
    w, b = perceptron(trainData, trainLabel, iter = 30)
    #进行测试,获得正确率
    accruRate = test(testData, testLabel, w, b)
    #获取当前时间,作为结束时间
    end = time.time()
    #显示正确率
    print('accuracy rate is:', accruRate)
    #显示用时时长
    print('time span:', end - start)

おすすめ

転載: blog.csdn.net/math_assistant/article/details/132010345