FizzBuzz問題をニューラルネットワークで解いてみる
TL;DR
FizzBuzz問題をニューラルネットワークで解いてみます。
ラベルデータの作成
import pandas as pd
results = []
for i in range(1, 10000 + 1):
if i % 3 == 0 and i % 5 == 0:
results.append((i, 'FizzBuzz'))
elif i % 3 == 0:
results.append((i, 'Fizz'))
elif i % 5 == 0:
results.append((i, 'Buzz'))
else:
results.append((i, 'Number'))
data_df = pd.DataFrame(results, columns=['Number', 'Results'])
display(data_df.head(15))
|
Number |
Results |
0 |
1 |
Number |
1 |
2 |
Number |
2 |
3 |
Fizz |
3 |
4 |
Number |
4 |
5 |
Buzz |
5 |
6 |
Fizz |
6 |
7 |
Number |
7 |
8 |
Number |
8 |
9 |
Fizz |
9 |
10 |
Buzz |
10 |
11 |
Number |
11 |
12 |
Fizz |
12 |
13 |
Number |
13 |
14 |
Number |
14 |
15 |
FizzBuzz |
前処理
feature_title = 'Number'
label_title = 'Results'
printable_labels = {k: i for i, k in enumerate(data_df[label_title].unique())}
class_count = len(printable_labels)
display(data_df.head(15))
display(data_df.describe())
display(printable_labels)
|
Number |
Results |
0 |
1 |
Number |
1 |
2 |
Number |
2 |
3 |
Fizz |
3 |
4 |
Number |
4 |
5 |
Buzz |
5 |
6 |
Fizz |
6 |
7 |
Number |
7 |
8 |
Number |
8 |
9 |
Fizz |
9 |
10 |
Buzz |
10 |
11 |
Number |
11 |
12 |
Fizz |
12 |
13 |
Number |
13 |
14 |
Number |
14 |
15 |
FizzBuzz |
|
Number |
count |
10000.00000 |
mean |
5000.50000 |
std |
2886.89568 |
min |
1.00000 |
25% |
2500.75000 |
50% |
5000.50000 |
75% |
7500.25000 |
max |
10000.00000 |
{'Number': 0, 'Fizz': 1, 'Buzz': 2, 'FizzBuzz': 3}
from keras import utils
labels = utils.np_utils.to_categorical([printable_labels[label] for label in data_df[label_title]], num_classes=class_count)
display(labels.shape)
display(labels)
C:\Users\hidek\Anaconda3\lib\site-packages\h5py\__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.
from ._conv import register_converters as _register_converters
Using TensorFlow backend.
(10000, 4)
array([[1., 0., 0., 0.],
[1., 0., 0., 0.],
[0., 1., 0., 0.],
...,
[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.]], dtype=float32)
import numpy as np
digits_count = 5
didgits_map = utils.np_utils.to_categorical(range(10), num_classes=10)
features = np.zeros((len(data_df), 5, 10), dtype = np.int32)
for i, number in enumerate(data_df[feature_title]):
for t, digit in enumerate(str(number).zfill(digits_count)[:]):
features[i, t] = didgits_map[int(digit)]
print(features.shape)
print(features)
(10000, 5, 10)
[[[1 0 0 ... 0 0 0]
[1 0 0 ... 0 0 0]
[1 0 0 ... 0 0 0]
[1 0 0 ... 0 0 0]
[0 1 0 ... 0 0 0]]
[[1 0 0 ... 0 0 0]
[1 0 0 ... 0 0 0]
[1 0 0 ... 0 0 0]
[1 0 0 ... 0 0 0]
[0 0 1 ... 0 0 0]]
[[1 0 0 ... 0 0 0]
[1 0 0 ... 0 0 0]
[1 0 0 ... 0 0 0]
[1 0 0 ... 0 0 0]
[0 0 0 ... 0 0 0]]
...
[[1 0 0 ... 0 0 0]
[0 0 0 ... 0 0 1]
[0 0 0 ... 0 0 1]
[0 0 0 ... 0 0 1]
[0 0 0 ... 0 1 0]]
[[1 0 0 ... 0 0 0]
[0 0 0 ... 0 0 1]
[0 0 0 ... 0 0 1]
[0 0 0 ... 0 0 1]
[0 0 0 ... 0 0 1]]
[[0 1 0 ... 0 0 0]
[1 0 0 ... 0 0 0]
[1 0 0 ... 0 0 0]
[1 0 0 ... 0 0 0]
[1 0 0 ... 0 0 0]]]
データの分割
from sklearn.model_selection import train_test_split
idx_features = range(len(data_df[feature_title]))
idx_labels = range(len(data_df[label_title]))
tmp_data = train_test_split(idx_features, idx_labels, train_size = 0.9, test_size = 0.1)
train_features = np.array([features[i] for i in tmp_data[0]])
valid_features = np.array([features[i] for i in tmp_data[1]])
train_labels = np.array([labels[i] for i in tmp_data[2]])
valid_labels = np.array([labels[i] for i in tmp_data[3]])
print(train_features.shape)
print(valid_features.shape)
print(train_labels.shape)
print(valid_labels.shape)
(9000, 5, 10)
(1000, 5, 10)
(9000, 4)
(1000, 4)
ネットワークの作成
from keras.models import Sequential
from keras.layers.core import Activation
from keras.layers import Dense, Dropout, LSTM, Embedding, Reshape, RepeatVector, Permute, Flatten, SimpleRNN
from keras.layers.wrappers import Bidirectional, TimeDistributed
from keras.optimizers import RMSprop
from keras.callbacks import LambdaCallback, EarlyStopping, ModelCheckpoint
from keras import layers
from keras.layers.normalization import BatchNormalization
from keras import Input, Model
model = Sequential()
model.add(Flatten(input_shape=(features[0].shape)))
model.add(Dense(2048, activation='relu'))
model.add(Dense(2048, activation='relu'))
model.add(Dense(class_count, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['mae'])
model.summary()
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
flatten_1 (Flatten) (None, 50) 0
_________________________________________________________________
dense_1 (Dense) (None, 2048) 104448
_________________________________________________________________
dense_2 (Dense) (None, 2048) 4196352
_________________________________________________________________
dense_3 (Dense) (None, 4) 8196
=================================================================
Total params: 4,308,996
Trainable params: 4,308,996
Non-trainable params: 0
_________________________________________________________________
学習
from keras.callbacks import LambdaCallback, EarlyStopping, ModelCheckpoint, TensorBoard
model_filename = 'models/fizzbuzzz-model.h5'
history = model.fit(train_features,
train_labels,
epochs = 300,
validation_split = 0.1,
batch_size = 256,
callbacks = [
TensorBoard(log_dir = 'logs'),
EarlyStopping(patience=5, monitor='val_mean_absolute_error'),
ModelCheckpoint(model_filename, monitor='val_mean_absolute_error', save_best_only=True)
])
Train on 8100 samples, validate on 900 samples
Epoch 1/300
8100/8100 [==============================] - 1s 137us/step - loss: 0.7933 - mean_absolute_error: 0.2498 - val_loss: 0.6256 - val_mean_absolute_error: 0.2134
Epoch 2/300
8100/8100 [==============================] - 0s 41us/step - loss: 0.6548 - mean_absolute_error: 0.2246 - val_loss: 0.6258 - val_mean_absolute_error: 0.2135
Epoch 3/300
8100/8100 [==============================] - 0s 39us/step - loss: 0.6422 - mean_absolute_error: 0.2232 - val_loss: 0.6292 - val_mean_absolute_error: 0.2229
Epoch 4/300
8100/8100 [==============================] - 0s 40us/step - loss: 0.6318 - mean_absolute_error: 0.2208 - val_loss: 0.6136 - val_mean_absolute_error: 0.2144
Epoch 5/300
8100/8100 [==============================] - 0s 41us/step - loss: 0.5816 - mean_absolute_error: 0.2072 - val_loss: 0.5271 - val_mean_absolute_error: 0.1884
[省略]
Epoch 297/300
8100/8100 [==============================] - 0s 40us/step - loss: 1.4231e-07 - mean_absolute_error: 3.3136e-08 - val_loss: 1.2583e-06 - val_mean_absolute_error: 6.0084e-07
Epoch 298/300
8100/8100 [==============================] - 0s 40us/step - loss: 1.4168e-07 - mean_absolute_error: 3.2659e-08 - val_loss: 1.2518e-06 - val_mean_absolute_error: 5.9729e-07
Epoch 299/300
8100/8100 [==============================] - 0s 39us/step - loss: 1.4122e-07 - mean_absolute_error: 3.2316e-08 - val_loss: 1.2392e-06 - val_mean_absolute_error: 5.9097e-07
Epoch 300/300
8100/8100 [==============================] - 0s 39us/step - loss: 1.4064e-07 - mean_absolute_error: 3.1852e-08 - val_loss: 1.2335e-06 - val_mean_absolute_error: 5.8808e-07
検証
from sklearn.metrics import classification_report, confusion_matrix
predicted_valid_labels = model.predict(valid_features).argmax(axis=1)
numeric_valid_labels = np.argmax(valid_labels, axis=1)
print(classification_report(numeric_valid_labels, predicted_valid_labels, target_names=printable_labels))
precision recall f1-score support
Number 1.00 1.00 1.00 551
Fizz 1.00 1.00 1.00 270
Buzz 1.00 1.00 1.00 119
FizzBuzz 1.00 1.00 1.00 60
avg / total 1.00 1.00 1.00 1000
Jupyter Notebook