# NeuralNetwork実践_MNIST | LittleVoice-g-string

LittleVoice-g-string

苦しむために生きないであなた自身を愛してくれ

NeuralNetwork実践_MNIST

説明

ニューラルネットワークを理解するため、Numpy、keras、Pytorchを使ってMNISTデータセットの手書き数字を識別してみました。

  • 使用しているデータ
    • Kaggle Digit Recognizer
    • MNISTデータセットは、機械学習でとても有名なデータセットであり、0から9までの手書き数字のグレースケール画像が入っています。各画像は、縦28X横28合計784ピクセルとなっており、各ピクセルには一つ0から255のピクセル値をついています。そのピクセル値が大きいければ大きいほど暗いことを意味しています。
    • Trainデータに関しては、先頭のlabel列はユーザーが描いた数字を表しており、残りの列は該当画像のピクセル値が格納されています。
    • testデータに関してはTrainデータと同様に、画像のピクセル値が格納していますが、label列がついていないので、それを識別するのは今回のタスクです。
  • training data,validate dataを用意します。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    data = np.array(train)
    m,n = data.shape
    np.random.shuffle(data)

    data_val=data[0:1000].T
    Y_val = data_val[0]
    X_val = data_val[1:n]
    X_val = X_val/255.
    print(data_val.shape,Y_val.shape,X_val.shape)

    data_train = data[1000:m].T
    Y_train = data_train[0]
    X_train = data_train[1:n]
    X_train = X_train/255.
    _,m_train = X_train.shape
    print(data_train.shape,Y_train.shape,X_train.shape)

    #(785, 1000) (1000,) (784, 1000)
    (785, 41000) (41000,) (784, 41000)

    1.EDA

    まずkaggleからダウンロードしたtrain.csvとtest.csvを読み込んで、データの中身をいろいろ確認してみました。
  1. モジュールをインポートします。
    1
    2
    3
    4
    5
    6
    7
    8
    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    import matplotlib.cm as cm
    from collections import Counter
    from pyg2plot import Plot
    from pyecharts.charts import Bar,Line
    from pyecharts import options as opts
  2. データ件数
    train:42000件訓練データ、785列、testデータと違って、label列がついています。
    test:28000件テストデータ、784列
    1
    2
    3
    4
    5
    6
    7
    train.shape,test.shape
    #((42000, 785), (28000, 784))

    for i in train.columns:
    if i not in test.columns:
    print(i)
    #label
  3. 実際にどういう数字なのか、画像で見てみます。
    1
    2
    3
    4
    5
    6
    num=train.iloc[10]
    pic=np.array(num)
    pic=pic[1:785]
    pic=pic.reshape(28,28)
    plt.imshow(pic,cmap="magma")
    plt.show()
  4. 全体のvalue値の幅を見てみます。
  • value値は0から255の間に落ちているので、後ほどの前処理ではvalue値を標準化処理にします。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    scatter = Plot("Scatter")
    df_scatter = pd.DataFrame(pic.reshape(392,2))
    df_scatter_dict = df_scatter.to_dict(orient = 'dict')
    scatter.set_options(
    {
    'appendPadding': 30,
    'data': df_scatter.to_dict(orient='records'),
    'xField': 0,
    'yField': 1,
    'size': [4, 30],
    'shape': 'circle',
    'pointStyle':{'fillOpacity': 0.8,'stroke': '#bbb'},
    'xAxis':{'line':{'style':{'stroke': '#aaa'}},},
    'yAxis':{'line':{'style':{'stroke': '#aaa'}},},
    'quadrant':{
    'xBaseline': 0,
    'yBaseline': 0,
    },
    })
    scatter.render()
  1. ラベルごとデータの件数を見てみます。
  • 各ラベルのデータ件数が大きいな差がなく、均等的であることがわかりました。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    bar = (
    Bar(init_opts=opts.InitOpts(width="620px", height="420px"))
    .add_xaxis(list(train_count.keys()))
    .add_yaxis('count', list(train_count.values()))
    .set_global_opts(title_opts=opts.TitleOpts(title="count", subtitle=None))
    .set_global_opts(title_opts={"text": "count", "subtext": None})
    )

    bar.render_notebook()

2.Numpyでニューラルネットワークを実装してみました

  1. 今回2層ニューラルネットワークを実装しようと思います。
    • 活性化関数:sigmoid関数(h1)とsoftmax関数(h2)
    • 損失関数:交差エントロピー誤差
  2. 事前に活性化関数、損失関数、labelのone-hot処理の関数を用意します。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #h1 sigmoid関数
    def sigmoid(x):
    return 1/(1+np.exp(-x))

    #back propagation用sigmoid関数の微分関数
    def sigmoid_(x):
    return sigmoid(x)*(1-sigmoid(x))

    #h2 softmax関数
    def softmax(z):
    A = np.exp(z)/sum(np.exp(z))
    return A

    #one-hot 処理関数
    def one_hot(Y):
    one_hot_Y = np.zeros((Y.size, Y.max() + 1))
    one_hot_Y[np.arange(Y.size), Y] = 1
    one_hot_Y = one_hot_Y.T
    return one_hot_Y

    #損失関数
    def cross_entropy(a, y):
    return np.sum(np.nan_to_num(-y*np.log(a)-(1-y)*np.log(1-a)))
  3. w1,b1,w2,b2の初期化
    1
    2
    3
    4
    5
    6
    7
    8
    input_, hidden, output = 784, 100, 10

    w1 = np.random.randn(hidden,input_)
    b1 = np.random.randn(hidden,1)
    w2 = np.random.randn(output,hidden)
    b2 = np.random.randn(output,1)
    print(w1.shape,b1.shape,w2.shape,b2.shape)
    #(100, 784) (100, 1) (10, 100) (10, 1)
  4. forwardの実現
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def forward(x,w1,w2,b1,b2):
    a1 = np.dot(w1,x)+b1
    z1 = sigmoid(a1)
    a2 = np.dot(w2,z1)+b2
    z2 = softmax(a2)
    y_pred = z2
    return a1,a2,z1,y_pred
    a1,a2,z1,y_pred = forward(X_train,w1,w2,b1,b2)
    print(a1.shape,a2.shape,z1.shape,y_pred.shape)
    #(100, 41000) (10, 41000) (100, 41000) (10, 41000)
  5. back propagatationの実現
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    def backward(z1,a1,y_pred,a2,x,y,w1,w2):
    dz2 = y_pred - one_hot(y)
    dw2 = (np.dot(dz2,z1.T))/m
    db2 = (1/m)*np.sum(dz2,axis=1,keepdims=True)
    da1 = np.dot(w2.T,dz2)
    dz1 = da1*sigmoid_(a1)

    dw1 =(1/m)*np.dot(dz1,x.T)
    db1 = (1/m)*np.sum(dz1,axis=1,keepdims=True)
    return dw1,db1,dw2,db2

    def update(w1,b1,w2,b2,dw1,db1,dw2,db2,lr):
    w2 -= lr*dw2
    b2 -= lr*db2
    w1 -= lr*dw1
    b1 -= lr*db1
    return w1,w2,b1,b2
  6. パラメータ最適化
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    # accuracy値の関数
    def get_accuracy(predictions,y):
    return np.sum(predictions == y)/y.size
    def gradient_descent(x,y,w1,w2,b1,b2,iterations,lr):
    accuracy_ = list()
    for i in range(iterations):
    a1,a2,z1,y_pred = forward(x,w1,w2,b1,b2)
    dw1,db1,dw2,db2 = backward(z1,a1,y_pred,a2,x,y,w1,w2)
    w1,w2,b1,b2=update(w1,b1,w2,b2,dw1,db1,dw2,db2,lr)
    if i % 10 == 0:
    print(f'第{(i/10)}回',i)
    predictions = np.argmax(a2,0)
    accuracy = round(get_accuracy(predictions,y),3)
    accuracy_.append(accuracy)
    print(accuracy)
    return w2,b2,w1,b1,accuracy,accuracy_
    w2,b2,w1,b1,accuracy,accuracy_ = gradient_descent(X_train,Y_train,w1,w2,b1,b2,5000,0.01)

    #第0.0回 0
    0.09409756097560976
    第1.0回 10
    0.1038780487804878
    第2.0回 20
    0.11382926829268293
    第3.0回 30
    0.1218780487804878
    ....
  7. accuracyの推移値
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    x_axis = [i for i in range(len(accuracy_))]
    y_axis = accuracy_
    line1=(
    Line()
    .add_xaxis(x_axis)
    .add_yaxis('accuracy',y_axis)
    .set_global_opts(title_opts=opts.TitleOpts(title='accuracy'),
    xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(font_size=20)))
    )
    line1.render_notebook()

3.Kerasでニューラルネットワークを構築てみました

  1. 考え方は基本Numpyでの構築と同様ですが、Sequentialモデルをもとに、.add()でレイヤーを積み重ねて、.compile()で訓練プロセスを設定します。
    1
    2
    3
    4
    5
    6
    7
    8
    model=Sequential() #
    model.add(Dense(100,activation = 'relu'))
    model.add(Dense(10,activation = 'softmax'))

    model.compile(optimizer='adam',
    loss ='sparse_categorical_crossentropy',
    metrics ='acc')
    model.fit(X_train.T,Y_train,epochs=60,batch_size=512)
  2. testデータの予測し、Kaggleに提出します。
    1
    2
    3
    result = model.predict(np.array(test))
    result_=pd.DataFrame(results,index=list(range(1,len(results)+1))).reset_index().rename(columns = {'index':'ImageId',0:'Label'})
    result_.to_csv('Digit Recognizer_version3(keras).csv',index=False)

4.Pytorchでニューラルネットワークを構築してみました

  1. データ集をPytorch専用のTensorに変換する
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import torch
    from torch.utils.data import DataLoader
    from torch import nn,optim
    import torch.nn.functional as F
    import torch.utils.data
    X_train_torch = torch.from_numpy(X_train).float()
    Y_train_torch = torch.from_numpy(Y_train).long()
    X_val_torch = torch.from_numpy(X_val).float()
    Y_val_torch = torch.from_numpy(Y_val).long()
    X_train_torch.T.size(0),Y_train_torch.T.size(0)
    #(41000, 41000)
  2. Batchごとを行うため、TensorDatasetに格納します。
    1
    2
    3
    4
    5
    train_dataset=torch.utils.data.TensorDataset(X_train_torch.T, Y_train_torch)
    val_dataset=torch.utils.data.TensorDataset(X_val_torch.T, Y_val_torch)

    train_loader = torch.utils.data.DataLoader(train_dataset,batch_size=1000,shuffle=True)
    val_loader = torch.utils.data.DataLoader(val_dataset,batch_size=1000,shuffle=True)
  3. Pytorchのフォーマットに沿って、ニューラルネットワークの構築を行います。過学習を防ぐため、Dropoutを利用します。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class NN(nn.Module):
    def __init__(self):
    super().__init__()
    self.layer1 = nn.Linear(784,100)
    self.layer2 = nn.Linear(100,10)

    self.dropout = nn.Dropout(p=0.2)

    def forward(self,x):
    x = x.view(x.shape[0],-1)

    x = self.dropout(F.relu(self.layer1(x)))
    x = F.softmax(self.layer2(x),dim=1)

    return x
  4.  損失関数と最適化手法を設定し、訓練プロセスを設定します。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    model = NN()

    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(),lr=0.001)

    epochs = 100
    train_losses = []
    test_losses = []
    accuracy_list = []
    print('start')
    model = model.float()
    for e in range(epochs):
    running_loss = 0
    for images,labels in train_loader:
    optimizer.zero_grad()

    log_ps = model(images)
    loss = criterion(log_ps,labels)
    loss.backward()
    optimizer.step()
    running_loss += loss.item()
    else:
    test_loss = 0
    accuracy = 0
    with torch.no_grad():
    model.eval()

    for images,labels in val_loader:
    log_ps =model(images)
    test_loss += criterion(log_ps,labels)
    ps = torch.exp(log_ps)
    top_p,top_class = ps.max(dim=1, keepdim=True)

    equals = top_class == labels.view(*top_class.shape)

    accuracy += torch.mean(equals.type(torch.FloatTensor))

    model.train()

    train_losses.append(running_loss/len(train_loader))
    test_losses.append(test_loss/len(val_loader))
    accuracy_list.append(accuracy)

    print("epoch: {}/{}.. ".format(e+1, epochs),
    "train_loss: {:.3f}.. ".format(running_loss/len(train_loader)),
    "test_loss: {:.3f}.. ".format(test_loss/len(val_loader)),
    "accuracy: {:.3f}".format(accuracy/len(val_loader)))
  5. 可視化によって、損失と精度の推移を確認します。
    1
    2
    3
    4
    plt.plot(train_losses,label='Training loss')
    plt.plot(test_losses,label='Validation loss')
    plt.plot(accuracy_list,label='Accuracy')
    plt.legend()

5.Next

  • Pytorchでテキストの分類を行ってみる

6.参考文献

  1. keras documents
  2. pytorch documents
  3. ゼロから作るDeepLearning

Welcome to my other publishing channels