本文通过参考《强化学习与深度学习 C 语言模拟》一书,来说明机器学习的原理。
进入正题,首先以一个划拳游戏来引入今天的主角——机器学习,剪刀石头布,相信大家伙都应该玩过,没有玩过的请教度娘,或关掉当前的网页。
程序代码比较简单,如下:
/* Visual Studio兼容性 */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define SEED 65535 /* 随机数SEED */
#define GU 0 /* 石头 */
#define CYOKI 1 /* 剪刀 */
#define PA 2 /* 布 */
#define WIN 1 /* 胜 */
#define LOSE -1 /* 负 */
#define DRAW 0 /* 平 */
#define ALPHA 0.01 /* 学习系数 */
/* 函数声明 */
int hand(double rate[]);
double frand(void);
int main()
{
int n = 0; /* 对战次数的计数器 */
int myhand, ohand; /* 自己的出拳和对手的出拳 */
double rate[3] = {1, 1, 1}; /* 出拳比率 */
int gain; /* 胜负结果 */
int payoffmatrix[3][3] = {{DRAW, WIN, LOSE}, /* 胜负矩阵 */
{LOSE, DRAW, WIN},
{WIN, LOSE, DRAW}};
while (scanf("%d", &ohand) != EOF)
{
if (ohand < GU || ohand > PA) /* 出拳不合法 */
{
continue;
}
myhand = hand(rate); /* 按照出拳比例进行出拳 */
gain = payoffmatrix[myhand][ohand]; /* 胜负判断 */
printf("%d %d %d ", myhand, ohand, gain); /* 输出结果 */
rate[myhand] += gain*ALPHA*rate[myhand]; /* 学习出拳比例 */
printf("%1f, %1f, %1f\n", rate[GU], rate[CYOKI], rate[PA]); /* 出拳比例输出 */
}
return 0;
}
/************************************************************************/
/* hnad() 函数 */
/* 随机数和其它参数一起出拳 */
/************************************************************************/
int hand(double rate[])
{
double gu, cyoki, pa; /* 出拳的对应值 */
gu = rate[GU]*frand();
cyoki = rate[CYOKI]*frand();
pa = rate[PA]*frand();
if(gu > cyoki)
{
if(gu>pa) return GU; /* gu大 */
else return PA; /* pa大 */
}
else
{
if(cyoki>pa) return CYOKI; /* cyoki大 */
else return PA; /* pa大 */
}
}
/************************************************************************/
/* frand() 函数 */
/* 0 - 1 的随机数 */
/************************************************************************/
double frand(void)
{
return (double)rand()/RAND_MAX;
}
话不多说,接下来,直接分析程序。
以下代码时得出模型的出拳类型,其中数组 rate 保存的是模型的出拳比率。初始化为 1 : 1 : 1。
double gu, cyoki, pa; /* 出拳的对应值 */
gu = rate[GU]*frand();
cyoki = rate[CYOKI]*frand();
pa = rate[PA]*frand();
if(gu > cyoki)
{
if(gu>pa) return GU; /* gu大 */
else return PA; /* pa大 */
}
else
{
if(cyoki>pa) return CYOKI; /* cyoki大 */
else return PA; /* pa大 */
}
有了出拳类型,接下来就是判断输赢胜负了,代码如下:
gain = payoffmatrix[myhand][ohand]; /* 胜负判断 */
根据胜负去不断调整模型出拳比率,也就是数组rate。
rate[myhand] += gain*ALPHA*rate[myhand]; /* 学习出拳比例 */
判断输赢的矩阵如下:
int payoffmatrix[3][3] = {{DRAW, WIN, LOSE}, /* 胜负矩阵 */
{LOSE, DRAW, WIN},
{WIN, LOSE, DRAW}};
payoffmatrix[][] 数组中,分别用我方出拳mahand和对手出拳ohand赋值,就可以得到胜负结果。例如 出拳为 “石头”,即 0
出拳为“剪刀” ,即 “1”,那么,
payoffmatrix[GU][CYOKI] —— payoffmatrix[0][1] ——WIN。
下面是我一直输入 “0”, 也就是我一直出石头,的程序输出结果,
可以看到,在我一直出石头的局面下, 石头比率一直等于 1, 剪刀比率不断减少,布的比率不断增大。
上面的例子虽然简单,但可以帮助我们更好地理解机器学习的原理和方法。