當前位置:
首頁 > 最新 > 使用ML.NET實現德州撲克牌型分類器

使用ML.NET實現德州撲克牌型分類器

本文將基於ML.NET v0.2預覽版,重點介紹提取特徵的思路和方法,實現德州撲克牌型分類器。

先介紹一下德州撲克的基本牌型,一手完整的牌共有五張撲克,10種牌型分別是:

1. 高牌,花色和點數同時沒有相同的牌。

2. 一對,點數有且僅有兩張相同的牌。

3. 兩對,兩張相同點數的牌,加另外兩張相同點數的牌。

4. 三條,有三張同一點數的牌。

5. 順子,五張順連的牌。

6. 同花,五張同一花色的牌。

7. 葫蘆,三張同一點數的牌,加一對其他點數的牌。

8. 四條,有四張同一點數的牌。

9. 同花順,同一花色五張順連的牌。

10. 皇家同花順,最高點數是A的同花順的牌。

這一次我們將使用邏輯回歸模型,來訓練數據完成我們想要的分類模型。

準備數據集

3,92,3,3,2,2,9,3,5,1

4,4,1,11,2,9,4,13,2,7,0

1,5,1,9,2,8,2,4,4,3,0

4,12,4,7,4,5,2,10,2,2,0

4,3,2,4,4,13,3,6,4,12,0

2,5,4,5,4,1,4,9,2,7,1

2,12,3,12,3,7,3,11,2,7,2

4,13,2,6,4,6,4,10,4,9,1

...

說明一下每一行的格式:

第1張花色,第1張點數,第2張花色,第2張點數,第3張花色,第3張點數,第4張花色,第4張點數,第5張花色,第5張點數,牌型

花色是1-4代表紅心,黑桃,方塊,梅花。點數1表示A,2-10保持不變,11表示J,12表示Q,13表示K。

特徵分析

前幾篇數據集的內容,基本上分割好就是特徵了,這一次不同,每一行的數值僅僅是元數據,也就是說,通過五張牌的花色和點數值是不能直接和牌型形成一一對應的聯繫,需要先按本文開頭介紹的10種牌型的描述,找到關鍵可數值化的欄位。因此,我選擇了這樣一些欄位:對子數,是否是三條,是否是四條,是否是順子,是否同花。通過這5個欄位值的組合,一定能判斷出牌型。

於是,我定義出我想要的數據類型:

public class PokerHandData

{

[Column(ordinal: "0")]

public float S1;

[Column(ordinal: "1")]

public float C1;

[Column(ordinal: "2")]

public float S2;

[Column(ordinal: "3")]

public float C2;

[Column(ordinal: "4")]

public float S3;

[Column(ordinal: "5")]

public float C3;

[Column(ordinal: "6")]

public float S4;

[Column(ordinal: "7")]

public float C4;

[Column(ordinal: "8")]

public float S5;

[Column(ordinal: "9")]

public float C5;

[Column(ordinal: "10", name: "Label")]

public float Power;

[Column(ordinal: "11")]

public float IsSameSuit;

[Column(ordinal: "12")]

public float IsStraight;

[Column(ordinal: "13")]

public float FourOfKind;

[Column(ordinal: "14")]

public float ThreeOfKind;

[Column(ordinal: "15")]

public float PairsCount;

}

S表示花色,C表示點數,Power表示牌型,PairsCount表示對子數,ThreeOfKind表示是否是三條,FourOfKind表示是否是四條,IsStraight表示是否順子,IsSameSuit表示是否同花。

判斷是否同花,只需要比較S1-S5的值即可。

public float GetIsSameSuit()

{

if (S1 == S2 && S2 == S3 && S3 == S4 && S4 == S5)

return 1;

else

return 0;

}

判斷其它幾個特徵,我需要一個通用方法,先統計出每一行的C1-C5,每種點數出現的次數。

static Dictionary GetValueCountsOfCondition(IEnumerable cards)

{

var dic = new Dictionary();

foreach (var item in cards)

{

if (dic.ContainsKey(item))

{

dic[item] += 1;

}

else

{

dic.Add(item, 1);

}

}

return dic;

}

然後再按特徵涵義計算值。

public float GetFourOfKind()

{

return GetCountOfCondition(4);

}

public float GetThreeOfKind()

{

return GetCountOfCondition(3);

}

public float GetPairsCount()

{

return GetCountOfCondition(2);

}

private IEnumerable GetCards()

{

if (cards == null)

{

cards = new[] { Convert.ToInt32(C1), Convert.ToInt32(C2), Convert.ToInt32(C3), Convert.ToInt32(C4), Convert.ToInt32(C5) };

}

return cards;

}

private float GetCountOfCondition(int target)

{

if (valueCounts == null)

{

valueCounts = GetValueCountsOfCondition(GetCards());

}

return valueCounts.Count(i => i.Value == target);

}

判斷是否為順子的方法,簡單而直接,就是看間隔差是不是為1,或者最高點有A剩下的必須是10、J、Q、K,都算順子。

public float GetIsStraight()

{

var keys = GetCards().ToArray();

Array.Sort(keys);

if (keys[1] - keys[0] == keys[2] - keys[1] && keys[2] - keys[1] == keys[3] - keys[2] && keys[3] - keys[2] == keys[4] - keys[3] && keys[4] - keys[3] == 1)

{

return 1;

}

else if (keys[0] == 1 && keys[1] == 10 && keys[2] == 11 && keys[3] == 12 && keys[4] == 13)

{

return 1;

}

else

{

return 0;

}

}

載入數據

這次由於使用了ML.NET v0.2,該版本的LearningPipeline新增了一種支持集合類型的數據源。因此,我將示範一種全新的載入數據集的方法,先以文件載入元數據,然後直接初始化特徵的值。

static IEnumerable

LoadData(string path)

{

using (var environment = new TlcEnvironment())

{

var pokerHandData = new List

();

var textLoader = new Microsoft.ML.Data.TextLoader(path).CreateFrom

(useHeader: false, separator: ",", trimWhitespace: false);

var experiment = environment.CreateExperiment();

var output = textLoader.ApplyStep(null, experiment) as ILearningPipelineDataStep;

experiment.Compile();

textLoader.SetInput(environment, experiment);

experiment.Run();

var data = experiment.GetOutput(output.Data);

using (var cursor = data.GetRowCursor((a => true)))

{

var getters = new ValueGetter[]{

cursor.GetGetter(0),

cursor.GetGetter(1),

cursor.GetGetter(2),

cursor.GetGetter(3),

cursor.GetGetter(4),

cursor.GetGetter(5),

cursor.GetGetter(6),

cursor.GetGetter(7),

cursor.GetGetter(8),

cursor.GetGetter(9),

cursor.GetGetter(10)

};

while (cursor.MoveNext())

{

float value0 = 0;

float value1 = 0;

float value2 = 0;

float value3 = 0;

float value4 = 0;

float value5 = 0;

float value6 = 0;

float value7 = 0;

float value8 = 0;

float value9 = 0;

float value10 = 0;

getters[0](ref value0);

getters[1](ref value1);

getters[2](ref value2);

getters[3](ref value3);

getters[4](ref value4);

getters[5](ref value5);

getters[6](ref value6);

getters[7](ref value7);

getters[8](ref value8);

getters[9](ref value9);

getters[10](ref value10);

var hands = new PokerHandData()

{

S1 = value0,

C1 = value1,

S2 = value2,

C2 = value3,

S3 = value4,

C3 = value5,

S4 = value6,

C4 = value7,

S5 = value8,

C5 = value9,

Power = value10

};

hands.Init();

pokerHandData.Add(hands);

}

}

return pokerHandData;

}

}

其中PokerHandData類增加一個初始化的方法。

public void Init()

{

IsSameSuit = GetIsSameSuit();

IsStraight = GetIsStraight();

FourOfKind = GetFourOfKind();

ThreeOfKind = GetThreeOfKind();

PairsCount = GetPairsCount();

}

訓練模型

預測的結構定義,以計分為目標,float[]類型表示是對每一種牌型有一個得分,分值越高屬於那一種牌型的概率越大。

publicclassPokerHandPrediction{ [ColumnName("Score")]

publicfloat[] PredictedPower;}

模型的選擇是LogisticRegressionClassifier,CollectionDataSource就是用來創建集合類型數據載入的對象。而特徵的指定不再是全部欄位,而是之前增加的那幾個。

public static PredictionModel

Train(IEnumerable

data)

{

var pipeline = new LearningPipeline();

var collection = CollectionDataSource.Create(data);

pipeline.Add(collection);

pipeline.Add(new ColumnConcatenator("Features", "IsSameSuit", "IsStraight", "FourOfKind", "ThreeOfKind", "PairsCount"));

pipeline.Add(new LogisticRegressionClassifier());

var model = pipeline.Train

();

return model;

}

預測結果

首先,對預測的得分,我們需要判斷一個概率傾向。

static string GetPower(float[] nums)

{

var index = -1;

var last = 0F;

for (int i = 0; i

{

if (nums[i] > last)

{

index = i;

last = nums[i];

}

}

var suit = string.Empty;

switch (index)

{

case 0:

suit = "高牌";

break;

case 1:

suit = "一對";

break;

case 2:

suit = "兩對";

break;

case 3:

suit = "三條";

break;

case 4:

suit = "順子";

break;

case 5:

suit = "同花";

break;

case 6:

suit = "葫蘆";

break;

case 7:

suit = "四條";

break;

case 8:

suit = "同花順";

break;

case 9:

suit = "皇家同花順";

break;

}

return suit;

}

最後就是進行預測的部分了。

public static void Predict(PredictionModel

model)

{

var test1 = new PokerHandData

{

S1 = 1,

C1 = 2,

S2 = 1,

C2 = 3,

S3 = 3,

C3 = 4,

S4 = 4,

C4 = 5,

S5 = 2,

C5 = 6

};

var test2 = new PokerHandData

{

S1 = 4,

C1 = 5,

S2 = 1,

C2 = 5,

S3 = 3,

C3 = 5,

S4 = 2,

C4 = 12,

S5 = 4,

C5 = 7

};

test1.Init();

test2.Init();

IEnumerable

pokerHands = new[]

{

test1,

test2

};

IEnumerable

predictions = model.Predict(pokerHands);

Console.WriteLine();

Console.WriteLine("PokerHand Predictions");

Console.WriteLine("---------------------");

var pokerHandsAndPredictions = pokerHands.Zip(predictions, (pokerHand, prediction) => (pokerHand, prediction));

foreach (var (pokerHand, prediction) in pokerHandsAndPredictions)

{

Console.WriteLine($"PokerHand: | Prediction: { GetPower(prediction.PredictedPower)}");

}

Console.WriteLine();

}

創建項目的步驟請參看本文開頭導讀給出的文章鏈接,不再贅述,運行結果如下:

最後放出源代碼文件:https://files.cnblogs.com/files/BeanHsiang/pokerhand.zip

希望讀者們保持對ML.NET的持續關注,相信新的特性一定能實現更複雜有趣的場景。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 全球大搜羅 的精彩文章:

有空嗎?我們一起去西雙版納吧
媳婦的這句話,讓婆婆永遠閉上了嘴

TAG:全球大搜羅 |