FizzBuzz 是一個常見的程式考題,題目很簡單,就是給一個整數,如果可以被 15 整除就回傳 FizzBuzz;可以被 3 整除就回傳 Fizz;被 5 整除就回傳 Buzz;都不能整除就回傳原本的數字。
用 Python 可以簡單幾行就寫出來:
1 | def fizz_buzz(num): |
不過有狂人就把這當作分類問題,用 tensorflow 來解這個問題,原文在此,是篇很有趣的文章 XD
由於原文是用 tensorflow 實作,我想我就來寫個 PyTorch 版練習一下吧!
基本上就是把 FizzBuzz 當作分類問題 (Classification) 來訓練,要做的事大概有這些:
- 準備 training, testing data
- 定義 model
- Training
那就來一步一步看看
準備資料
雖然 FizzBuzz 輸入是一個整數,但是把他轉成二進位會比較好訓練,所以先來寫個轉二進位的函式:
1 | def encode(num): |
因為我不想 import numpy
,所以這邊轉二進位的方式是用 Python 的 format 來做。
另外還要把 FizzBuzz 改寫成回傳分類號碼:
1 | def fizz_buzz(num): |
接下來要來產生資料拉
1 | def make_data(num_of_data, batch_size): |
由於 training 的時候通常會是一批一批 (batch) 下去訓練的,所以在準備資料時就先一批一批放在一起會比較方便。
所以改一下,
1 | def make_data(num_of_data, batch_size): |
前置步驟都弄好之後,終於可以來產生訓練跟測試資料拉,Batch size 就訂個 32 好了:
1 | training_data = make_data(1000, 32) |
定義 Model
其實我對於如何設計 model 還是沒有很了解,不過這問題應該是挺簡單的,弄個幾層 fully-connected layer 應該就夠了吧?
1 | class FizzBuzz(nn.Module): |
我用了一層隱藏層,1024 個神經元,activation function 則都是最基本的 ReLU 。in_channel
, out_channel
分別是輸入數字是長度多少的二進位 (10),以及輸出幾種分類 (4)。
PyTorch 的 model 是繼承 torch.nn.Module
來寫個 class,通常只要定義 __init()__
跟 forward()
就好,如果要自己做特殊的 backward 的話,也可以實作 backward()
。
Training
整個訓練的過程基本就是按照一般的分類問題流程做,把資料丟進 model 的到預測,把預測跟正確答案做 cross entropy 當作 loss ,然後去最小化這個 loss
用 PyTorch 寫大概是這樣:
1 | def training(model, optimizer, training_data): |
由於 要是 Variable
才能自動算 back propagation ,所以 data 跟 label 都要變成 Variable
。
這邊我用的 optimize 方法是 Stochastic Gradient Descent (SGD),記得每次都要先清空 gradient 再做 backward。
Result
萬事皆備,可以開始來看看結果如何了,來 train 300 個 Epoch 好了,
1 | ==== Start Training ==== |
哇!才 98% 準確率呢… 拿去 online judge 解題大概不會過呢 XD
如果想要玩玩看我的 code,這邊可以看:
https://github.com/SSARCandy/pytorch_fizzbuzz