本来计划这篇博客写如何用web前端写2048,但是鉴于以前的代码写得太乱,暂时并不打算继续这个话题了;
本篇的主题是“感知器及其C++的实现”,我会简单介绍模式识别中的感知器是个什么东西,并且会给出我写的C++代码
进入正题:
什么是感知器?
引出:
比如说某银行的分析软件有这样一项功能,通过输入某客户的个人信息(年龄、性别、职业、收入、欠债情况、信用情况等),返回是否给该客户提供信用卡服务;对于这个问题,我们可以定义某个函数f可以完成这样的功能,但是这个f是未知的;但是通过很多先验数据(已经通过人工判定是否发放行用卡),我们可以估计出这样一个函数g(宏观上来说就和人一样,当你对于某个事件见得多了,下次你就能预见其可能状态了),它能够做出和f相似的判定;这就是数据驱动。
其中此例子的数据是指:通过人工判定是否发放信用卡的数据,称之为是带有标签的。
对于是否发放信用卡此类问题,只有两种结果:发或不发;对于这样的,只有两种状态的问题,我们可以用一个线性感知器来完成。
怎么操作呢?
首先我们做一个转换,将数据都转换成可以计算的量,不妨都装换为数字,比如有一个这样的用户jerry,其用户数据如下
{
yearsold: 20, //年龄
salary: 10000, //月收入
debt:100000 //欠债
}
可能客户不同的信息在是否发放信用卡这件事上占有不同的比重,那么我们可以给每条数据的每个信息加上一个比重w求和(加权求和)。则可以得出一个总的权重:
y = yearsold * w1 + salary * w2 + debt * w3;
容易想到,可以设定一个阈值(threshold)判定是否发放信用卡,则做如下假设:
yearsold * w1 + salary * w2 + debt * w3 > threshold,则发放信用卡
yearsold * w1 + salary * w2 + debt * w3 < threshold,则不发放信用卡
再把这个式子转化为yearsold * w1 + salary * w2 + debt * w3 + (-1)*threshold ? 0;其中?表示">" 或 “<”
习惯用矩阵或者向量形式表示:(w1, w2, w3, threshold)·(yearsold, salary, debt, -1) ? 0,可以用符号函数在形式上简化函数:g(x) = sign((w1, w2, w3, threshold)·(yearsold, salary, debt, -1) );其中x表示输入的数据,是一个一维或者多维数据;且记w = (w1, w2, w3, threshold) 。
现在开始,似乎已经有某种方法得出一系列权重的来判定是否发放信用卡了。当时,到底怎么来获取这些权重呢?如何才能讲我有的数据都用上呢?
有一种迭代的方式(怎么来的我也不知道,但是这种方法确实能够将所有数据都用上,并且加入新的数据时不用全部重新计算,或许你能够提出新的方法):
w(n+1) = w(n) + f(n)x(n), 当g(n)≠f(n)的时候用这个式子更新w,否则w不变(因为此时的w对x(n)已经能够正确区分了)
其中w(n)表示第n次迭代的w, f(n)表示第n条数据的标签(发不发行用卡可以用-1(不发) 和 +1(发)表示),x(n)表示第n条数据;
但是这样有一个问题,什么时候停止迭代呢?遍历完所有数据吗?这样似乎不太好,因为遍历完所有数据也不能保证w是好的。可以这样做:
1、设置一个迭代上限,如果在此迭代上限内能够正确区分所有数据,则停止迭代,否则一直迭代,知道迭代次数达到上限;
2、看相邻两次w所带来的误差,如果两次误差变化不大,则说明已经不能够再优化w(这个是模仿计算方法提出的,不一定可行,且需要提出一个误差的计算函数)
如果数据时线性可分的,用线性分类器可以得到好的结果,如果线性不可分则不能直接得到好的结果。
到此为止,线性感知器的介绍就结束了。
接下来是代码部分:
#pragma once #include <iostream> #include <vector> class PLA { public: std::vector<double> _w; int _dataSum, _wSum; bool _pokeFlag; unsigned long int _pokeErrors; unsigned long int _pokePos; PLA(int, double); PLA(); PLA(std::vector< std::vector<double> >, double); ~PLA(); void InitVectorW(int, double); void LoadMark(std::vector<int>); bool LoadData(std::vector< std::vector<double> >); bool Train(unsigned long int); private: std::vector< std::vector<double> > _data; protected: std::vector<double> _singleData; std::vector<int> _markVector; int _mark; int _index; bool Perce(int, int); bool ReadNextData(); };
#include "PLA.h" #include "Vector.h" PLA::PLA() { _index = 0; _dataSum = 0; _wSum = 0; } PLA::PLA(int len, double init) { _index = 0; _dataSum = 0; _wSum = 0; for (int i = 0; i < len + 1; i++) _w.push_back(init); _wSum = len + 1; } PLA::PLA(std::vector< std::vector<double> > data, double init) { _index = 0; int dataSum = data.size(); if (dataSum) { int len = data[0].size(); for (int i = 0; i < len + 1; i++) _w.push_back(init); LoadVector(data, _data); _wSum = len + 1; _dataSum = dataSum; } else return; } PLA::~PLA() { } bool PLA::Perce(int part1, int part2) { using namespace std; double ans = VectorMul(_singleData, _w); //std::cout << ans << std::endl; int flagTemp = (ans >= 0) ? part1 : part2; bool flag = ((flagTemp) == _mark) ? true : false; //cout << flagTemp << " " << _mark << endl; if (!flag) { //std::cout << flagTemp << " " << _mark << std::endl; //cout << "!!" << endl; VectorAdd(_w, _singleData, _mark); //int i; //for (i = 0; i < _w.size(); i++) //{ // _w[i] += _singleData[i] * _mark; //} return false; } return true; } void PLA::InitVectorW(int len, double init) { for (int i = 0; i < len + 1; i++) _w.push_back(init); _wSum = len + 1; } bool PLA::LoadData(std::vector< std::vector<double> > data) { int dataSum = data.size(); if (dataSum) { int len = data[0].size(); LoadVector(data, _data); _wSum = len + 1 ; _dataSum = dataSum; return true; } else return false; } void PLA::LoadMark(std::vector<int> markVector) { for (int i = 0; i < markVector.size(); i++) _markVector.push_back(markVector[i]); } bool PLA::ReadNextData() { _singleData.clear(); _mark = _markVector[_index]; for (int i = 0; i < _data[_index].size(); i++) { _singleData.push_back(_data[_index][i]); //std::cout << _data[_index].size() << std::endl; } _singleData.push_back(1); /*for (int i = 0; i < _singleData.size(); i ++) { using namespace std; cout << _singleData[i] << " "; } std::cout << std::endl;*/ _index++; return true; } bool PLA::Train(unsigned long int loop) { unsigned long int error_pre = 10000000; int count = 0; bool pokeFlag = false; std::vector<double> wTemp; for (unsigned long int i = 0; i < loop; i++) { unsigned long int error = 0; _index = 0; //std::cout << "dataSum " << _dataSum << std::endl; for (int j = 0; j < _dataSum; j++) { ReadNextData(); if (!Perce(1, -1)) error++; } if (error > 1) { if (error < error_pre) { error_pre = error; wTemp.clear(); for (int len = 0; len < _w.size(); len++) wTemp.push_back(_w[len]); std::cout << error_pre << std::endl; pokeFlag = true; _pokePos = i; _pokeErrors = error; } } else { pokeFlag = false; break; } //std::cout << error << std::endl; } if (pokeFlag) { _w.clear(); for (int i = 0; i < wTemp.size(); i++) _w.push_back(wTemp[i]); } _pokeFlag = pokeFlag; return pokeFlag; }
错误之处,还望指出!