感知器及其C++的实现

本来计划这篇博客写如何用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;
}

错误之处,还望指出!

猜你喜欢

转载自blog.csdn.net/cbc000/article/details/78837994