近幾年科技領(lǐng)域很流行大數(shù)據(jù)、機(jī)器學(xué)習(xí)登馒、深度學(xué)習(xí)之類的字眼匙握,其實(shí),這些東西原理很早的時候就有了陈轿,只不過當(dāng)時不叫這些名詞圈纺,等我們慢慢學(xué)習(xí)就會發(fā)現(xiàn),這些玩意兒結(jié)合了高數(shù)麦射、概率統(tǒng)計(jì)學(xué)蛾娶、矩陣論、線性代數(shù)等潜秋,然后以代碼實(shí)現(xiàn)來解決實(shí)際問題蛔琅。深度學(xué)習(xí)可以認(rèn)為被包含在機(jī)器學(xué)習(xí)之中。至于目前他們的應(yīng)用和研究領(lǐng)域峻呛,包括圖像識別罗售、NLP(自然語言處理)等等,大家自行查閱钩述。廢話不多說寨躁,現(xiàn)在我從一個最簡單的模型向大家展示這類玩意兒怎么玩起來
1.感知機(jī)
為了了解什么是感知機(jī),我們需要先理解一下幾點(diǎn):
第一點(diǎn):感知機(jī)是二類分類的線性分類模型(為了新手理解牙勘,還是多說兩句朽缎,什么叫做二類分類,從程序的角度谜悟,感知機(jī)的輸入是某個事物话肖,而它的輸出只有兩種狀態(tài),從現(xiàn)實(shí)的角度葡幸,我們把具體的人這個事物輸入給感知機(jī)要她判別性別最筒,最后感知機(jī)只能輸出兩種結(jié)果:“男人”或“女人”,不會有第三種~~蔚叨,如果我們需要有第三種結(jié)果床蜘,則這個問題不能用感知機(jī)來解決(有其他辦法)辙培。至于什么叫做線性,接下來會講到)
第二點(diǎn):把某個事物輸入到感知機(jī)模型的理解邢锯。(太抽象了吧扬蕊,映射到程序是怎樣的啊)這種輸入其實(shí)有很多種形式丹擎,我舉個最簡單的例子幫助讀者理解尾抑。對于某個特定的人,比如我蒂培,身高2.0米再愈,體重150,帥氣程度100分(滿分100) 可愛程度0(滿分100)护戳,數(shù)據(jù)提取出來 (2.0 150 100 0) 其中的每一個標(biāo)識都稱之為一個特征點(diǎn)翎冲,然后我們就定義一個向量 a = (2.0,150,100,0)來表示“我”這個人,所以輸入就是向量a(特征向量)媳荒。(ps:這只是最基礎(chǔ)的一種形式抗悍,不局限于此)
第三點(diǎn):“學(xué)習(xí)”的簡單理解。機(jī)器學(xué)習(xí)钳枕、深度學(xué)習(xí)中都有學(xué)習(xí)二字缴渊,此二字代表什么意思?直觀理解么伯,就是我們從已知信息中學(xué)習(xí)其中的規(guī)律規(guī)則疟暖,從而預(yù)測未知。還是以上述例子闡述√锶幔現(xiàn)在我們碰到這樣的一個問題:我有1000條人的信息俐巴,現(xiàn)在我在路上看到一個背影,目測身高165,體重90硬爆、帥氣程度30欣舵,可愛程度80,想知道他(她)是男是女缀磕。這種情況就可以考慮采用感知機(jī)處理類似問題缘圈。(埋下鋪墊,實(shí)際中的應(yīng)用場景很有限)袜蚕。我們提取1000個人信息中的五個特征點(diǎn)(身高糟把、體重、帥氣程度牲剃、可愛程度遣疯,性別)轉(zhuǎn)化成1000個特征向量,1000個標(biāo)記向量(只有性別凿傅,男用+1表示缠犀,女用-1表示数苫,對應(yīng)于特征向量)作為感知機(jī)的輸入進(jìn)行訓(xùn)練,讓其學(xué)習(xí)其中的關(guān)聯(lián)辨液,得到一個訓(xùn)練后的模型虐急,當(dāng)輸入向量(1.65,90,30,80)后,感知機(jī)就可以根據(jù)訓(xùn)練好的模型預(yù)測出最后的結(jié)果(+1 或 -1)
第四點(diǎn):我們從數(shù)學(xué)的角度深入推敲這個問題的實(shí)際轉(zhuǎn)化(其實(shí)也就是一個數(shù)學(xué)建模的過程)
可以理解如下
人 ? ? ? ? ? ? ? 特征向量 ? ? ? ? ? ? ? ? ? ? ? ?標(biāo)記 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?序號
我 ? ? ? ? ? (2.0,150,100,0) ? ? ? ? ? ? ?+1(男) ? ? ? ? ? 已知 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?1
夏目 ? ? (1.78,135,80,20) ? ? ? ? +1 ( 男) ? ? ? ? ? 已知 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?2
魯路修 ?(1.80,140,99,1) ? ? ? ? ?+1(男) ? ? ? ? ?已知 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?3
魔女cc ?(1.63,88,10,99) ? ? ? ? ? -1 (女) ? ? ? ? 已知 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?4
.... ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?... ? ? ? ? ? ? ? ? ? ? ?... ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ...
路人 ? ? ?(1.65,90,20,90) ? ? ? ? ? ? ?(要求解的量) ??
那么滔迈,建模開始
第五點(diǎn):"學(xué)習(xí)"的數(shù)學(xué)表達(dá)式直觀理解(以下以感知機(jī)為例止吁,其實(shí)很多類型神經(jīng)網(wǎng)絡(luò)都類似)
通過上述建模,現(xiàn)在我們的問題變成了如何調(diào)整系數(shù)W(w0,w1,w2.... )和b亡鼠,(注意赏殃,此處的系數(shù)W是向量)敷待,使得模型f(X) = sign(W*X+b)具有更好的表達(dá)能力间涵。此處好的表達(dá)能力指的什么?還是舉上述列子榜揖,對于已知的1000個特征向量勾哩,輸入模型f計(jì)算后得到的結(jié)果盡可能和對應(yīng)標(biāo)簽一致(其實(shí)也就是一個擬合過程),然后我們再輸入表示剛才路人的特征向量举哟,經(jīng)過f計(jì)算的結(jié)果就是對于此路人的性別預(yù)測結(jié)果思劳。
那么,現(xiàn)在問題變成了如何調(diào)整系數(shù)W和b妨猩,使得f(X)學(xué)習(xí)到的表達(dá)能力最好潜叛。這里扯起來又可以寫幾篇文章,我們以后一步步展開壶硅,目前我只做陳述威兜,有興趣的同學(xué)可以查看我的csdn博客(代價函數(shù) )。目前咱們暫且要記住庐椒,如何調(diào)整系數(shù)W和b椒舵,使得f(X)學(xué)習(xí)到的表達(dá)能力最好問題,可以轉(zhuǎn)換為調(diào)整模型參數(shù)W约谈,b笔宿,使得代價函數(shù)最小。代價函數(shù)有許多定義形式棱诱,此處我們依照李航《統(tǒng)計(jì)學(xué)習(xí)方法》P26 P27(大家有興趣的可以看看泼橘,其中有cost代價函數(shù)的推導(dǎo)過程。此處我直接給出
其中M表示誤分類點(diǎn)的集合 迈勋,xi表示第i個樣本(第i個已知信息的特征向量)炬灭,yi對應(yīng)+1/-1
第六點(diǎn):“學(xué)習(xí)算法”
怎樣使得代價函數(shù)最小(其實(shí)變成了一個純高數(shù)問題)粪躬,對于L(W,b)担败,xi和yi已知昔穴,W和b是變量,L是一個多元方程提前,求W和b吗货,使得L為最小值,采用梯度下降算法(ps:高數(shù)中有一個梯度的概念狈网,大家需要回顧宙搬,可以參考機(jī)器學(xué)習(xí)-梯度下降法實(shí)現(xiàn)線性回歸 )
最后的步驟如下:
上述訓(xùn)練集中xi可以是多維的,即訓(xùn)練集T = {(x11,x12,x13,x14,...,x1n,y1),(x21,x22,x23,x24,...,x2n,y2,...,(xN1,xN2,xN3,xN4,...,xNn,yN))
讀者要特別注意的幾點(diǎn):上述文字只是幫助初學(xué)者更容易上手理解拓哺,沒有涉及推導(dǎo)過程勇垛,《統(tǒng)計(jì)學(xué)習(xí)方法》李航著 ?第一二章一定要看看,學(xué)會手工推算士鸥,損失函數(shù)闲孤、梯度下降都要分別查閱文獻(xiàn)和推導(dǎo)
讀到這估計(jì)很多人已經(jīng)迷糊了,但最大的問題在于烤礁,為什么建模的時候使用多元線性方程讼积,難道多元自變量和結(jié)果之間的關(guān)系一定是線性的嗎?(如果讀到這都沒想過這個問題脚仔,那~~)當(dāng)然不一定勤众,怎么可能,而且絕大部分不是線性可分的鲤脏,所以们颜,大家要記住,單個感知機(jī)的表達(dá)能力很弱猎醇。此處可以聯(lián)想一下窥突,如果在二維坐標(biāo)中有很多離散的紅藍(lán)點(diǎn),我們有多少概率可以用一條線就將其劃分開姑食,在三維坐標(biāo)中又有多少概率可以用平面將其劃分開波岛,依次類推到n維。既然單個感知機(jī)的能力這么挫音半,我為什么要開篇講它则拷? 因?yàn)閱蝹€感知機(jī)可以看成組成神經(jīng)網(wǎng)絡(luò)的神經(jīng)元的一部分,以后詳解曹鸠。
2.代碼實(shí)例
扯了半天不寫代碼和咸魚又有什么區(qū)別
代碼下載(整個項(xiàng)目打包了煌茬,安裝vs2013 ,直接雙擊.sln即可調(diào)試查看)
如果我們用上述代碼學(xué)習(xí)訓(xùn)練無法線性可分的數(shù)據(jù)集彻桃,就會導(dǎo)致W坛善,b無法收斂,陷入死循環(huán),若一定要用感知機(jī)訓(xùn)練眠屎,就只能手工設(shè)置循環(huán)次數(shù)剔交,但這種方式最后會導(dǎo)致訓(xùn)練效果不好,預(yù)測的正確率就會大幅降低改衩。以后會更新更好的神經(jīng)網(wǎng)絡(luò)模型來解決這個問題
以統(tǒng)計(jì)學(xué)習(xí)方法P29頁題目為例
#include<iostream>
#include<vector>
#include<string>
#include<fstream>
using namespace std;
typedef vectorfeatureVct;
typedef int label;
class Perceptron;
void printRes(Perceptron pp);
class Perceptron
{private:
vectorFtSet; //特征向量數(shù)據(jù)集vectorlabelSet; //二分類label數(shù)據(jù)集 +1或-1
double LearnRate; //學(xué)習(xí)率
featureVct w; //感知機(jī)模型參數(shù)
double bias; //偏置項(xiàng)
int dimension; //特征向量維度
int count;
public:
Perceptron(int dimension,featureVct w,double bias = 0.0,double learnRate = 1);
int GetCount() const;? //獲取迭代次數(shù)
const featureVct& GetW() const ; ? ? //獲取模型參數(shù)
void SetW(const featureVct& w); ? //設(shè)置模型參數(shù)
double GetBias() const; //獲取偏置項(xiàng)
void SetBias(double bias); //設(shè)置騙置項(xiàng)
bool ReadSource(const string& filePath); //讀取數(shù)據(jù)源
void Train(); //訓(xùn)練模型岖常,得到感知機(jī)模型
public:
double VectorDotProduct(const featureVct& f1, const featureVct& f2);? //向量點(diǎn)積運(yùn)算)
featureVct VectorScalarMulti(double num, const featureVct f); //向量數(shù)乘運(yùn)算
featureVct VectorAdd(const featureVct& f1, const featureVct& f2);? //向量加法運(yùn)算
};
const featureVct& Perceptron::GetW() const
{
return w;
}
void Perceptron::SetW(const featureVct& w)
{
this->w = w;
}
double Perceptron::GetBias() const
{
return bias;
}
void Perceptron::SetBias(double bias)
{
this->bias = bias;
}
/*文件格式:每行表示一個樣本點(diǎn),特征值之間用空格隔開葫督,最后一列存儲類別信息1或-1*/
bool Perceptron::ReadSource(const string& filePath)
{
ifstream file(filePath);
if (!file)
return false;
while (!file.eof())
{
featureVct dataTmp;
double tmp;
for (int i = 0; i < dimension; ++i)
{
file >> tmp;
dataTmp.push_back(tmp);
}
FtSet.push_back(dataTmp);
label labelData;
file >> labelData;
labelSet.push_back(labelData);
}
return true;
}
void Perceptron::Train()
{
//感知機(jī)訓(xùn)練過程
int flag = true;
while (flag)
{
for (int i = 0; i < FtSet.size(); i++)
{
flag = false;
if (labelSet[i] * (VectorDotProduct(w,FtSet[i]) + bias) <= 0)
{
//此處的打印只是為了更直觀的給大家展示學(xué)習(xí)過程竭鞍,工程中最后不要直接在類中打印信息
printRes(*this);
flag = true;
w = VectorAdd(w, VectorScalarMulti(labelSet[i]*LearnRate,FtSet[i]));
bias += LearnRate * labelSet[i];
++count;
break;
}
}
//此處的打印只是為了更直觀的給大家展示學(xué)習(xí)過程,工程中最后不要直接在類中打印信息
if(!flag)? printRes(*this);
}
}
Perceptron::Perceptron(int dimension, featureVct w, double bias, double learnRate)
{
this->dimension = dimension;
this->bias = bias;
this->LearnRate = learnRate;
this->w = w;
count = 0;
}
double Perceptron::VectorDotProduct(const featureVct& f1, const featureVct& f2)
{
double sum = 0.0;
for (int i = 0; i != f1.size(); ++i)
{
sum += f1[i] * f2[i];
}
return sum;
}
featureVct Perceptron::VectorScalarMulti(double num, const featureVct f)
{
featureVct tmp;
for (int i = 0; i != f.size(); ++i)
{
tmp.push_back(num*f[i]);
}
return tmp;
}
featureVct Perceptron::VectorAdd(const featureVct& f1, const featureVct& f2)
{
featureVct tmp(0);
for (int i = 0; i != f1.size(); ++i)
{
tmp.push_back(f1[i] + f2[i]);
}
return tmp;
}
int Perceptron::GetCount() const
{
return count;
}
void printRes(Perceptron pp)
{
cout << "迭代次數(shù):" << pp.GetCount() << endl;
cout << "w:";
featureVct tmp = pp.GetW();
for (int i = 0; i < tmp.size() - 1; ++i)
{
cout << tmp[i] << " ";
}
cout << tmp[tmp.size() - 1] << endl;
cout << "bias:";
cout << pp.GetBias() << endl;
cout << "---------------------------" << endl;
}
int main()
{
featureVct w;
w.push_back(0.0); //設(shè)置w初始化參數(shù)
w.push_back(0.0);
Perceptron pp(2, w);? //創(chuàng)建特征向量為2維的感知機(jī)對象
if (!pp.ReadSource("sun.txt"))
{
cout << "讀取文件失敗";
exit(-1);
}
pp.Train();? //訓(xùn)練得到感知機(jī)模型
}