Showing
27 changed files
with
764 additions
and
0 deletions
simple_convnet/.DS_Store
0 → 100644
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
simple_convnet/cifar10-test.gz
0 → 100644
This file is too large to display.
simple_convnet/cifar10-train.gz
0 → 100644
This file is too large to display.
simple_convnet/dataset/.DS_Store
0 → 100644
No preview for this file type
simple_convnet/dataset/__init__.py
0 → 100644
File mode changed
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
simple_convnet/dataset/cifar10.py
0 → 100644
1 | +# coding: utf-8 | ||
2 | +try: | ||
3 | + import urllib.request | ||
4 | +except ImportError: | ||
5 | + raise ImportError('You should use Python 3.x') | ||
6 | +import os.path | ||
7 | +import gzip | ||
8 | +import pickle | ||
9 | +import os | ||
10 | +import numpy as np | ||
11 | + | ||
12 | + | ||
13 | +key_file = { | ||
14 | + 'train':'cifar10-train.gz', | ||
15 | + 'test':'cifar10-test.gz' | ||
16 | +} | ||
17 | + | ||
18 | +dataset_dir = os.path.dirname(os.path.abspath('/Users/HyeonJun/Desktop/simple_convnet/dataset')) | ||
19 | +save_file = dataset_dir + "/cifar10.pkl" | ||
20 | + | ||
21 | +train_num = 50000 | ||
22 | +test_num = 10000 | ||
23 | +img_dim = (3, 32, 32) | ||
24 | +img_size = 3072 | ||
25 | + | ||
26 | +def _load_label(file_name): | ||
27 | + file_path = dataset_dir + "/" + file_name | ||
28 | + | ||
29 | + print("Converting " + file_name + " to NumPy Array ...") | ||
30 | + with gzip.open(file_path, 'rb') as f: | ||
31 | + labels = np.frombuffer(f.read(), np.uint8, offset=0) | ||
32 | + labels = labels.reshape(-1, img_size+1) | ||
33 | + labels = labels.T | ||
34 | + print("Done") | ||
35 | + | ||
36 | + return labels[0] | ||
37 | + | ||
38 | +def _load_img(file_name): | ||
39 | + file_path = dataset_dir + "/" + file_name | ||
40 | + | ||
41 | + print("Converting " + file_name + " to NumPy Array ...") | ||
42 | + with gzip.open(file_path, 'rb') as f: | ||
43 | + data = np.frombuffer(f.read(), np.uint8, offset=0) | ||
44 | + data = data.reshape(-1, img_size+1) | ||
45 | + data = np.delete(data, 0, 1) | ||
46 | + print("Done") | ||
47 | + | ||
48 | + return data | ||
49 | + | ||
50 | +def _convert_numpy(): | ||
51 | + dataset = {} | ||
52 | + dataset['train_img'] = _load_img(key_file['train']) | ||
53 | + dataset['train_label'] = _load_label(key_file['train']) | ||
54 | + dataset['test_img'] = _load_img(key_file['test']) | ||
55 | + dataset['test_label'] = _load_label(key_file['test']) | ||
56 | + | ||
57 | + return dataset | ||
58 | + | ||
59 | +def init_cifar10(): | ||
60 | + dataset = _convert_numpy() | ||
61 | + print("Creating pickle file ...") | ||
62 | + with open(save_file, 'wb') as f: | ||
63 | + pickle.dump(dataset, f, -1) | ||
64 | + print("Done!") | ||
65 | + | ||
66 | +def _change_one_hot_label(X): | ||
67 | + T = np.zeros((X.size, 10)) | ||
68 | + for idx, row in enumerate(T): | ||
69 | + row[X[idx]] = 1 | ||
70 | + | ||
71 | + return T | ||
72 | + | ||
73 | +def load_cifar10(normalize=True, flatten=True, one_hot_label=False): | ||
74 | + """CIFAR-10データセットの読み込み | ||
75 | + | ||
76 | + Parameters | ||
77 | + ---------- | ||
78 | + normalize : 画像のピクセル値を0.0~1.0に正規化する | ||
79 | + one_hot_label : | ||
80 | + one_hot_labelがTrueの場合、ラベルはone-hot配列として返す | ||
81 | + one-hot配列とは、たとえば[0,0,1,0,0,0,0,0,0,0]のような配列 | ||
82 | + flatten : 画像を一次元配列に平にするかどうか | ||
83 | + | ||
84 | + Returns | ||
85 | + ------- | ||
86 | + (訓練画像, 訓練ラベル), (テスト画像, テストラベル) | ||
87 | + """ | ||
88 | + if not os.path.exists(save_file): | ||
89 | + init_cifar10() | ||
90 | + | ||
91 | + with open(save_file, 'rb') as f: | ||
92 | + dataset = pickle.load(f) | ||
93 | + | ||
94 | + if normalize: | ||
95 | + for key in ('train_img', 'test_img'): | ||
96 | + dataset[key] = dataset[key].astype(np.float32) | ||
97 | + dataset[key] /= 255.0 | ||
98 | + | ||
99 | + if one_hot_label: | ||
100 | + dataset['train_label'] = _change_one_hot_label(dataset['train_label']) | ||
101 | + dataset['test_label'] = _change_one_hot_label(dataset['test_label']) | ||
102 | + | ||
103 | + if not flatten: | ||
104 | + for key in ('train_img', 'test_img'): | ||
105 | + dataset[key] = dataset[key].reshape(-1, 3, 32, 32) | ||
106 | + | ||
107 | + return (dataset['train_img'], dataset['train_label']), (dataset['test_img'], dataset['test_label']) | ||
108 | + | ||
109 | + | ||
110 | +if __name__ == '__main__': | ||
111 | + init_cifar10() |
simple_convnet/functions.py
0 → 100644
1 | +# coding: utf-8 | ||
2 | +#import cupy as cp | ||
3 | +import numpy as cp | ||
4 | +import numpy as np | ||
5 | + | ||
6 | + | ||
7 | +def identity_function(x): | ||
8 | + return x | ||
9 | + | ||
10 | + | ||
11 | +def step_function(x): | ||
12 | + return np.array(x > 0, dtype=np.int) | ||
13 | + | ||
14 | + | ||
15 | +def sigmoid(x): | ||
16 | + return 1 / (1 + np.exp(-x)) | ||
17 | + | ||
18 | + | ||
19 | +def sigmoid_grad(x): | ||
20 | + return (1.0 - sigmoid(x)) * sigmoid(x) | ||
21 | + | ||
22 | + | ||
23 | +def relu(x): | ||
24 | + return np.maximum(0, x) | ||
25 | + | ||
26 | + | ||
27 | +def relu_grad(x): | ||
28 | + grad = np.zeros(x) | ||
29 | + grad[x>=0] = 1 | ||
30 | + return grad | ||
31 | + | ||
32 | + | ||
33 | +def softmax(x): | ||
34 | + if x.ndim == 2: | ||
35 | + x = x.T | ||
36 | + x = x - cp.max(x, axis=0) | ||
37 | + y = cp.exp(x, dtype=np.float32) / cp.sum(cp.exp(x, dtype=np.float32), axis=0, dtype=np.float32) | ||
38 | + return y.T | ||
39 | + | ||
40 | + x = x - cp.max(x) # オーバーフロー対策 | ||
41 | + return cp.exp(x) / cp.sum(cp.exp(x)) | ||
42 | + | ||
43 | + | ||
44 | +def mean_squared_error(y, t): | ||
45 | + return 0.5 * np.sum((y-t)**2) | ||
46 | + | ||
47 | + | ||
48 | +def cross_entropy_error(y, t): | ||
49 | + if y.ndim == 1: | ||
50 | + t = t.reshape(1, t.size) | ||
51 | + y = y.reshape(1, y.size) | ||
52 | + | ||
53 | + # 教師データがone-hot-vectorの場合、正解ラベルのインデックスに変換 | ||
54 | + if t.size == y.size: | ||
55 | + t = t.argmax(axis=1) | ||
56 | + | ||
57 | + batch_size = y.shape[0] | ||
58 | + return -cp.sum(cp.log(y[cp.arange(batch_size), t])) / batch_size | ||
59 | + | ||
60 | + | ||
61 | +def softmax_loss(X, t): | ||
62 | + y = softmax(X) | ||
63 | + return cross_entropy_error(y, t) |
simple_convnet/gradient.py
0 → 100644
1 | +# coding: utf-8 | ||
2 | +import numpy as np | ||
3 | + | ||
4 | +def _numerical_gradient_1d(f, x): | ||
5 | + h = 1e-4 # 0.0001 | ||
6 | + grad = np.zeros_like(x) | ||
7 | + | ||
8 | + for idx in range(x.size): | ||
9 | + tmp_val = x[idx] | ||
10 | + x[idx] = float(tmp_val) + h | ||
11 | + fxh1 = f(x) # f(x+h) | ||
12 | + | ||
13 | + x[idx] = tmp_val - h | ||
14 | + fxh2 = f(x) # f(x-h) | ||
15 | + grad[idx] = (fxh1 - fxh2) / (2*h) | ||
16 | + | ||
17 | + x[idx] = tmp_val | ||
18 | + | ||
19 | + return grad | ||
20 | + | ||
21 | + | ||
22 | +def numerical_gradient_2d(f, X): | ||
23 | + if X.ndim == 1: | ||
24 | + return _numerical_gradient_1d(f, X) | ||
25 | + else: | ||
26 | + grad = np.zeros_like(X) | ||
27 | + | ||
28 | + for idx, x in enumerate(X): | ||
29 | + grad[idx] = _numerical_gradient_1d(f, x) | ||
30 | + | ||
31 | + return grad | ||
32 | + | ||
33 | + | ||
34 | +def numerical_gradient(f, x): | ||
35 | + h = 1e-4 # 0.0001 | ||
36 | + grad = np.zeros_like(x) | ||
37 | + | ||
38 | + it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite']) | ||
39 | + while not it.finished: | ||
40 | + idx = it.multi_index | ||
41 | + tmp_val = x[idx] | ||
42 | + x[idx] = float(tmp_val) + h | ||
43 | + fxh1 = f(x) # f(x+h) | ||
44 | + | ||
45 | + x[idx] = tmp_val - h | ||
46 | + fxh2 = f(x) # f(x-h) | ||
47 | + grad[idx] = (fxh1 - fxh2) / (2*h) | ||
48 | + | ||
49 | + x[idx] = tmp_val # 値を元に戻す | ||
50 | + it.iternext() | ||
51 | + | ||
52 | + return grad | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
simple_convnet/layers.py
0 → 100644
This diff is collapsed. Click to expand it.
simple_convnet/optimizer.py
0 → 100644
1 | + | ||
2 | +import numpy as cp | ||
3 | +import numpy as np | ||
4 | + | ||
5 | +class SGD: | ||
6 | + | ||
7 | + | ||
8 | + def __init__(self, lr=0.01): | ||
9 | + self.lr = lr | ||
10 | + | ||
11 | + def update(self, params, grads): | ||
12 | + for key in params.keys(): | ||
13 | + params[key] -= self.lr * grads[key] | ||
14 | + | ||
15 | + | ||
16 | +class Momentum: | ||
17 | + | ||
18 | + | ||
19 | + def __init__(self, lr=0.01, momentum=0.9): | ||
20 | + self.lr = lr | ||
21 | + self.momentum = momentum | ||
22 | + self.v = None | ||
23 | + | ||
24 | + def update(self, params, grads): | ||
25 | + if self.v is None: | ||
26 | + self.v = {} | ||
27 | + for key, val in params.items(): | ||
28 | + self.v[key] = np.zeros_like(val) | ||
29 | + | ||
30 | + for key in params.keys(): | ||
31 | + self.v[key] = self.momentum*self.v[key] - self.lr*grads[key] | ||
32 | + params[key] += self.v[key] | ||
33 | + | ||
34 | + | ||
35 | +class Nesterov: | ||
36 | + | ||
37 | + | ||
38 | + def __init__(self, lr=0.01, momentum=0.9): | ||
39 | + self.lr = lr | ||
40 | + self.momentum = momentum | ||
41 | + self.v = None | ||
42 | + | ||
43 | + def update(self, params, grads): | ||
44 | + if self.v is None: | ||
45 | + self.v = {} | ||
46 | + for key, val in params.items(): | ||
47 | + self.v[key] = np.zeros_like(val) | ||
48 | + | ||
49 | + for key in params.keys(): | ||
50 | + self.v[key] *= self.momentum | ||
51 | + self.v[key] -= self.lr * grads[key] | ||
52 | + params[key] += self.momentum * self.momentum * self.v[key] | ||
53 | + params[key] -= (1 + self.momentum) * self.lr * grads[key] | ||
54 | + | ||
55 | + | ||
56 | +class AdaGrad: | ||
57 | + | ||
58 | + | ||
59 | + def __init__(self, lr=0.01): | ||
60 | + self.lr = lr | ||
61 | + self.h = None | ||
62 | + | ||
63 | + def update(self, params, grads): | ||
64 | + if self.h is None: | ||
65 | + self.h = {} | ||
66 | + for key, val in params.items(): | ||
67 | + self.h[key] = np.zeros_like(val) | ||
68 | + | ||
69 | + for key in params.keys(): | ||
70 | + self.h[key] += grads[key] * grads[key] | ||
71 | + params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7) | ||
72 | + | ||
73 | + | ||
74 | +class RMSprop: | ||
75 | + | ||
76 | + | ||
77 | + def __init__(self, lr=0.01, decay_rate = 0.99): | ||
78 | + self.lr = lr | ||
79 | + self.decay_rate = decay_rate | ||
80 | + self.h = None | ||
81 | + | ||
82 | + def update(self, params, grads): | ||
83 | + if self.h is None: | ||
84 | + self.h = {} | ||
85 | + for key, val in params.items(): | ||
86 | + self.h[key] = np.zeros_like(val) | ||
87 | + | ||
88 | + for key in params.keys(): | ||
89 | + self.h[key] *= self.decay_rate | ||
90 | + self.h[key] += (1 - self.decay_rate) * grads[key] * grads[key] | ||
91 | + params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7) | ||
92 | + | ||
93 | + | ||
94 | +class Adam: | ||
95 | + | ||
96 | + | ||
97 | + def __init__(self, lr=0.001, beta1=0.9, beta2=0.999): | ||
98 | + self.lr = lr | ||
99 | + self.beta1 = beta1 | ||
100 | + self.beta2 = beta2 | ||
101 | + self.iter = 0 | ||
102 | + self.m = None | ||
103 | + self.v = None | ||
104 | + | ||
105 | + def update(self, params, grads): | ||
106 | + if self.m is None: | ||
107 | + self.m, self.v = {}, {} | ||
108 | + for key, val in params.items(): | ||
109 | + self.m[key] = cp.zeros_like(val, dtype=np.float32) | ||
110 | + self.v[key] = cp.zeros_like(val, dtype=np.float32) | ||
111 | + | ||
112 | + self.iter += 1 | ||
113 | + lr_t = self.lr * cp.sqrt(1.0 - self.beta2**self.iter, dtype=np.float32) / (1.0 - self.beta1**self.iter) | ||
114 | + | ||
115 | + for key in params.keys(): | ||
116 | + | ||
117 | + self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key]) | ||
118 | + self.v[key] += (1 - self.beta2) * (grads[key]**2 - self.v[key]) | ||
119 | + | ||
120 | + params[key] -= lr_t * self.m[key] / (cp.sqrt(self.v[key], dtype=np.float32) + 1e-7) | ||
121 | + | ||
122 | + | ||
123 | +class EarlyStopping(): | ||
124 | + def __init__(self, patience=0, verbose=0): | ||
125 | + self.step = 0 | ||
126 | + self.acc = 0.0 | ||
127 | + self.patience = patience | ||
128 | + self.verbose = verbose | ||
129 | + | ||
130 | + def validate(self, acc): | ||
131 | + if self.acc > acc: | ||
132 | + self.step += 1 | ||
133 | + if self.step > self.patience: | ||
134 | + if self.verbose: | ||
135 | + print('early stopping') | ||
136 | + return True | ||
137 | + else: | ||
138 | + self.step = 0 | ||
139 | + self.acc = acc | ||
140 | + return False |
simple_convnet/param.py
0 → 100644
simple_convnet/params.pkl
0 → 100644
No preview for this file type
simple_convnet/simple_convnet4.py
0 → 100644
1 | +import sys, os | ||
2 | +sys.path.append(os.pardir) | ||
3 | +import pickle | ||
4 | +import numpy as cp | ||
5 | +import numpy as np | ||
6 | +from collections import OrderedDict | ||
7 | +from layers import * | ||
8 | +from gradient import numerical_gradient | ||
9 | + | ||
10 | + | ||
11 | +class SimpleConvNet: | ||
12 | + def __init__(self, input_dim=(3, 32, 32), | ||
13 | + conv_param={'filter_num':(32, 32, 64), 'filter_size':3, 'pad':1, 'stride':1}, | ||
14 | + hidden_size=512, output_size=10, weight_init_std=0.01): | ||
15 | + filter_num = conv_param['filter_num'] | ||
16 | + filter_size = conv_param['filter_size'] | ||
17 | + filter_pad = conv_param['pad'] | ||
18 | + filter_stride = conv_param['stride'] | ||
19 | + input_size = input_dim[1] | ||
20 | + conv_output_size = (input_size - filter_size + 2*filter_pad) / filter_stride + 1 | ||
21 | + conv_data_size = int(filter_num[0] * conv_output_size * conv_output_size ) | ||
22 | + pool1_output_size = int(filter_num[1] * (conv_output_size/2) * (conv_output_size/2)) | ||
23 | + pool2_output_size = int(filter_num[2] * (conv_output_size/4) * (conv_output_size/4)) | ||
24 | + pool3_output_size = int(filter_num[2] * (conv_output_size/8) * (conv_output_size/8)) | ||
25 | + | ||
26 | + self.params = {} | ||
27 | + self.params['W1'] = cp.array( weight_init_std * \ | ||
28 | + cp.random.randn(filter_num[0], input_dim[0], filter_size, filter_size), dtype=np.float32) | ||
29 | + | ||
30 | + self.params['W2'] = cp.array( weight_init_std * \ | ||
31 | + cp.random.randn(filter_num[1], filter_num[0], 1, 1), dtype=np.float32) | ||
32 | + | ||
33 | + self.params['W3'] = cp.array( weight_init_std * \ | ||
34 | + cp.random.randn(filter_num[1], 1, filter_size, filter_size), dtype=np.float32) | ||
35 | + | ||
36 | + self.params['W4'] = cp.array( weight_init_std * \ | ||
37 | + cp.random.randn(filter_num[2], filter_num[1], 1, 1), dtype=np.float32) | ||
38 | + | ||
39 | + self.params['W5'] = cp.array( weight_init_std * \ | ||
40 | + cp.random.randn(filter_num[2], 1, filter_size, filter_size), dtype=np.float32) | ||
41 | + | ||
42 | + self.params['W6'] = cp.array( weight_init_std * \ | ||
43 | + cp.random.randn(pool3_output_size, hidden_size), dtype=np.float32) | ||
44 | + | ||
45 | + self.params['W7'] = cp.array( weight_init_std * \ | ||
46 | + cp.random.randn(hidden_size, output_size), dtype=np.float32) | ||
47 | + | ||
48 | + self.layers = OrderedDict() | ||
49 | + self.layers['Conv1'] = Convolution(self.params['W1'], | ||
50 | + conv_param['stride'], conv_param['pad']) | ||
51 | + self.layers['LightNorm1'] = LightNormalization() | ||
52 | + self.layers['Relu1'] = Relu() | ||
53 | + self.layers['Pool1'] = Pooling(pool_h=2, pool_w=2, stride=2) | ||
54 | + | ||
55 | + self.layers['Conv2'] = Convolution(self.params['W2'], | ||
56 | + 1, 0) | ||
57 | + self.layers['LightNorm2'] = LightNormalization() | ||
58 | + self.layers['Relu2'] = Relu() | ||
59 | + self.layers['Conv3'] = DW_Convolution(self.params['W3'], | ||
60 | + conv_param['stride'], conv_param['pad']) | ||
61 | + self.layers['LightNorm3'] = LightNormalization() | ||
62 | + self.layers['Relu3'] = Relu() | ||
63 | + self.layers['Pool2'] = Pooling(pool_h=2, pool_w=2, stride=2) | ||
64 | + | ||
65 | + self.layers['Conv4'] = Convolution(self.params['W4'], | ||
66 | + 1, 0) | ||
67 | + self.layers['LightNorm4'] = LightNormalization() | ||
68 | + self.layers['Relu4'] = Relu() | ||
69 | + self.layers['Conv5'] = DW_Convolution(self.params['W5'], | ||
70 | + conv_param['stride'], conv_param['pad']) | ||
71 | + self.layers['LightNorm5'] = LightNormalization() | ||
72 | + self.layers['Relu5'] = Relu() | ||
73 | + self.layers['Pool3'] = Pooling(pool_h=2, pool_w=2, stride=2) | ||
74 | + | ||
75 | + self.layers['Affine4'] = Affine(self.params['W6']) | ||
76 | + self.layers['LightNorm6'] = LightNormalization() | ||
77 | + self.layers['Relu6'] = Relu() | ||
78 | + | ||
79 | + self.layers['Affine5'] = Affine(self.params['W7']) | ||
80 | + | ||
81 | + self.last_layer = SoftmaxWithLoss() | ||
82 | + | ||
83 | + def predict(self, x): | ||
84 | + for layer in self.layers.values(): | ||
85 | + x = layer.forward(x) | ||
86 | + | ||
87 | + return x | ||
88 | + | ||
89 | + def loss(self, x, t): | ||
90 | + y = self.predict(x) | ||
91 | + return self.last_layer.forward(y, t) | ||
92 | + | ||
93 | + def accuracy(self, x, t, batch_size=100): | ||
94 | + if t.ndim != 1 : t = np.argmax(t, axis=1) | ||
95 | + | ||
96 | + acc = 0.0 | ||
97 | + | ||
98 | + for i in range(int(x.shape[0] / batch_size)): | ||
99 | + tx = x[i*batch_size:(i+1)*batch_size] | ||
100 | + tt = t[i*batch_size:(i+1)*batch_size] | ||
101 | + y = self.predict(tx) | ||
102 | + y = np.argmax(y, axis=1) | ||
103 | + acc += np.sum(y == tt) #numpy | ||
104 | + | ||
105 | + return acc / x.shape[0] | ||
106 | + | ||
107 | + def gradient(self, x, t): | ||
108 | + | ||
109 | + self.loss(x, t) | ||
110 | + | ||
111 | + dout = 1 | ||
112 | + dout = self.last_layer.backward(dout) | ||
113 | + | ||
114 | + layers = list(self.layers.values()) | ||
115 | + layers.reverse() | ||
116 | + for layer in layers: | ||
117 | + dout = layer.backward(dout) | ||
118 | + | ||
119 | + grads = {} | ||
120 | + grads['W1'] = self.layers['Conv1'].dW | ||
121 | + grads['W2'] = self.layers['Conv2'].dW | ||
122 | + grads['W3'] = self.layers['Conv3'].dW | ||
123 | + grads['W4'] = self.layers['Conv4'].dW | ||
124 | + grads['W5'] = self.layers['Conv5'].dW | ||
125 | + grads['W6'] = self.layers['Affine4'].dW | ||
126 | + grads['W7'] = self.layers['Affine5'].dW | ||
127 | + return grads | ||
128 | + | ||
129 | + def save_params(self, file_name="params.pkl"): | ||
130 | + params = {} | ||
131 | + for key, val in self.params.items(): | ||
132 | + params[key] = val | ||
133 | + with open(file_name, 'wb') as f: | ||
134 | + pickle.dump(params, f) | ||
135 | + |
simple_convnet/train_convnet.py
0 → 100644
1 | +# coding: utf-8 | ||
2 | +import sys, os | ||
3 | +sys.path.append(os.pardir) | ||
4 | +import time | ||
5 | +import numpy as np | ||
6 | +from dataset.cifar10 import load_cifar10 | ||
7 | +from simple_convnet4 import SimpleConvNet | ||
8 | +from trainer import Trainer | ||
9 | + | ||
10 | +(x_train, t_train), (x_test, t_test) = load_cifar10(flatten=False) | ||
11 | + | ||
12 | +test_mask = np.random.choice(x_test.shape[0], 1000) | ||
13 | +x_test = x_test[test_mask] | ||
14 | +t_test = t_test[test_mask] | ||
15 | + | ||
16 | +max_epochs = 30 | ||
17 | + | ||
18 | +network = SimpleConvNet(input_dim=(3,32,32), | ||
19 | + conv_param = {'filter_num': (32, 32, 64), 'filter_size': 3, 'pad': 1, 'stride': 1}, | ||
20 | + hidden_size=512, output_size=10, weight_init_std=0.01) | ||
21 | + | ||
22 | +trainer = Trainer(network, x_train, t_train, x_test, t_test, | ||
23 | + epochs=max_epochs, mini_batch_size=100, | ||
24 | + optimizer='Adam', optimizer_param={'lr': 0.001}, | ||
25 | + evaluate_sample_num_per_epoch=1000, early_stopping=5) | ||
26 | +start = time.time() | ||
27 | +trainer.train() | ||
28 | +elapsed_time = time.time() - start | ||
29 | +print ("elapsed_time:{0}".format(elapsed_time) + "[sec]") | ||
30 | + | ||
31 | +network.save_params("params.pkl") | ||
32 | +print("Saved Network Parameters!") | ||
33 | + | ||
34 | +markers = {'train': 'o', 'test': 's'} | ||
35 | +x = np.arange(trainer.current_epoch) | ||
36 | +plt.plot(x, trainer.train_acc_list, marker='o', label='train', markevery=2) | ||
37 | +plt.plot(x, trainer.test_acc_list, marker='s', label='test', markevery=2) | ||
38 | +plt.xlabel("epochs") | ||
39 | +plt.ylabel("accuracy") | ||
40 | +plt.ylim(0, 1.0) | ||
41 | +plt.legend(loc='lower right') | ||
42 | +plt.show() |
simple_convnet/trainer.py
0 → 100644
1 | +# coding: utf-8 | ||
2 | +import sys, os | ||
3 | +sys.path.append(os.pardir) # 親ディレクトリのファイルをインポートするための設定 | ||
4 | +import numpy as np | ||
5 | +from optimizer import * | ||
6 | + | ||
7 | +class Trainer: | ||
8 | + """ニューラルネットの訓練を行うクラス | ||
9 | + """ | ||
10 | + def __init__(self, network, x_train, t_train, x_test, t_test, | ||
11 | + epochs=20, mini_batch_size=100, | ||
12 | + optimizer='SGD', optimizer_param={'lr':0.01}, | ||
13 | + evaluate_sample_num_per_epoch=None, early_stopping=5, verbose=True): | ||
14 | + self.network = network | ||
15 | + self.verbose = verbose | ||
16 | + self.x_train = x_train | ||
17 | + self.t_train = t_train | ||
18 | + self.x_test = x_test | ||
19 | + self.t_test = t_test | ||
20 | + self.epochs = epochs | ||
21 | + self.batch_size = mini_batch_size | ||
22 | + self.evaluate_sample_num_per_epoch = evaluate_sample_num_per_epoch | ||
23 | + | ||
24 | + # optimzer | ||
25 | + optimizer_class_dict = {'sgd':SGD, 'momentum':Momentum, 'nesterov':Nesterov, | ||
26 | + 'adagrad':AdaGrad, 'rmsprpo':RMSprop, 'adam':Adam} | ||
27 | + self.optimizer = optimizer_class_dict[optimizer.lower()](**optimizer_param) | ||
28 | + self.early_stopping = EarlyStopping(patience=early_stopping, verbose=self.verbose) | ||
29 | + | ||
30 | + self.train_size = x_train.shape[0] | ||
31 | + self.iter_per_epoch = max(self.train_size / mini_batch_size, 1) | ||
32 | + self.max_iter = int(epochs * self.iter_per_epoch) | ||
33 | + self.current_iter = 0 | ||
34 | + self.current_epoch = 0 | ||
35 | + | ||
36 | + self.train_loss_list = [] | ||
37 | + self.train_acc_list = [] | ||
38 | + self.test_acc_list = [] | ||
39 | + | ||
40 | + def train_step(self): | ||
41 | + early_stopping = False | ||
42 | + batch_mask = np.random.choice(self.train_size, self.batch_size) | ||
43 | + x_batch = self.x_train[batch_mask] | ||
44 | + t_batch = self.t_train[batch_mask] | ||
45 | + | ||
46 | + grads = self.network.gradient(x_batch, t_batch) | ||
47 | + self.optimizer.update(self.network.params, grads) | ||
48 | + | ||
49 | + loss = self.network.loss(x_batch, t_batch) | ||
50 | + self.train_loss_list.append(loss) | ||
51 | + if self.verbose: print(str(self.current_epoch) + " : " + str(int(self.current_iter % self.iter_per_epoch)) + " : train loss:" + str(loss)) | ||
52 | + | ||
53 | + if self.current_iter % self.iter_per_epoch == 0: | ||
54 | + self.current_epoch += 1 | ||
55 | + | ||
56 | + x_train_sample, t_train_sample = self.x_train, self.t_train | ||
57 | + x_test_sample, t_test_sample = self.x_test, self.t_test | ||
58 | + if not self.evaluate_sample_num_per_epoch is None: | ||
59 | + t = self.evaluate_sample_num_per_epoch | ||
60 | + x_train_sample, t_train_sample = self.x_train[:t], self.t_train[:t] | ||
61 | + x_test_sample, t_test_sample = self.x_test[:t], self.t_test[:t] | ||
62 | + | ||
63 | + train_acc = self.network.accuracy(x_train_sample, t_train_sample) | ||
64 | + test_acc = self.network.accuracy(x_test_sample, t_test_sample) | ||
65 | + self.train_acc_list.append(train_acc) | ||
66 | + self.test_acc_list.append(test_acc) | ||
67 | + early_stopping = self.early_stopping.validate(test_acc) | ||
68 | + | ||
69 | + if self.verbose: print("=== epoch:" + str(self.current_epoch) + ", train acc:" + str(train_acc) + ", test acc:" + str(test_acc) + " ===") | ||
70 | + self.current_iter += 1 | ||
71 | + return early_stopping | ||
72 | + | ||
73 | + def train(self): | ||
74 | + for i in range(self.max_iter): | ||
75 | + if self.train_step(): | ||
76 | + break | ||
77 | + | ||
78 | + test_acc = self.network.accuracy(self.x_test, self.t_test) | ||
79 | + | ||
80 | + if self.verbose: | ||
81 | + print("=============== Final Test Accuracy ===============") | ||
82 | + print("test acc:" + str(test_acc)) | ||
83 | + |
simple_convnet/util.py
0 → 100644
1 | +# coding: utf-8 | ||
2 | +#import cupy as cp | ||
3 | +import numpy as cp | ||
4 | +import numpy as np | ||
5 | + | ||
6 | +def DW_im2col(input_data, filter_h, filter_w, stride=1, pad=0): | ||
7 | + """다수의 이미지를 입력받아 2차원 배열로 변환한다(평탄화). | ||
8 | + | ||
9 | + Parameters | ||
10 | + ---------- | ||
11 | + input_data : 4차원 배열 형태의 입력 데이터(이미지 수, 채널 수, 높이, 너비) | ||
12 | + filter_h : 필터의 높이 | ||
13 | + filter_w : 필터의 너비 | ||
14 | + stride : 스트라이드 | ||
15 | + pad : 패딩 | ||
16 | + | ||
17 | + Returns | ||
18 | + ------- | ||
19 | + col : 2차원 배열 | ||
20 | + """ | ||
21 | + N, C, H, W = input_data.shape | ||
22 | + out_h = (H + 2 * pad - filter_h) // stride + 1 | ||
23 | + out_w = (W + 2 * pad - filter_w) // stride + 1 | ||
24 | + | ||
25 | + img = np.pad(input_data, [(0, 0), (0, 0), (pad, pad), (pad, pad)], 'constant') | ||
26 | + col = np.zeros((N, C, filter_h, filter_w, out_h, out_w)) | ||
27 | + | ||
28 | + for y in range(filter_h): | ||
29 | + y_max = y + stride * out_h | ||
30 | + for x in range(filter_w): | ||
31 | + x_max = x + stride * out_w | ||
32 | + col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride] | ||
33 | + | ||
34 | + col = col.transpose(1, 0, 4, 5, 2, 3).reshape(C, N * out_h * out_w, -1) | ||
35 | + return col | ||
36 | + | ||
37 | +def smooth_curve(x): | ||
38 | + """損失関数のグラフを滑らかにするために用いる | ||
39 | + | ||
40 | + 参考:http://glowingpython.blogspot.jp/2012/02/convolution-with-numpy.html | ||
41 | + """ | ||
42 | + window_len = 11 | ||
43 | + s = np.r_[x[window_len-1:0:-1], x, x[-1:-window_len:-1]] | ||
44 | + w = np.kaiser(window_len, 2) | ||
45 | + y = np.convolve(w/w.sum(), s, mode='valid') | ||
46 | + return y[5:len(y)-5] | ||
47 | + | ||
48 | + | ||
49 | +def shuffle_dataset(x, t): | ||
50 | + """データセットのシャッフルを行う | ||
51 | + | ||
52 | + Parameters | ||
53 | + ---------- | ||
54 | + x : 訓練データ | ||
55 | + t : 教師データ | ||
56 | + | ||
57 | + Returns | ||
58 | + ------- | ||
59 | + x, t : シャッフルを行った訓練データと教師データ | ||
60 | + """ | ||
61 | + permutation = np.random.permutation(x.shape[0]) | ||
62 | + x = x[permutation,:] if x.ndim == 2 else x[permutation,:,:,:] | ||
63 | + t = t[permutation] | ||
64 | + | ||
65 | + return x, t | ||
66 | + | ||
67 | +def conv_output_size(input_size, filter_size, stride=1, pad=0): | ||
68 | + return (input_size + 2*pad - filter_size) / stride + 1 | ||
69 | + | ||
70 | + | ||
71 | +def im2col(input_data, filter_h, filter_w, stride=1, pad=0): | ||
72 | + """ | ||
73 | + | ||
74 | + Parameters | ||
75 | + ---------- | ||
76 | + input_data : (データ数, チャンネル, 高さ, 幅)の4次元配列からなる入力データ | ||
77 | + filter_h : フィルターの高さ | ||
78 | + filter_w : フィルターの幅 | ||
79 | + stride : ストライド | ||
80 | + pad : パディング | ||
81 | + | ||
82 | + Returns | ||
83 | + ------- | ||
84 | + col : 2次元配列 | ||
85 | + """ | ||
86 | + N, C, H, W = input_data.shape | ||
87 | + out_h = (H + 2*pad - filter_h)//stride + 1 | ||
88 | + out_w = (W + 2*pad - filter_w)//stride + 1 | ||
89 | + | ||
90 | + img = cp.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant') | ||
91 | + col = cp.zeros((N, C, filter_h, filter_w, out_h, out_w), dtype=np.float32) | ||
92 | + | ||
93 | + for y in range(filter_h): | ||
94 | + y_max = y + stride*out_h | ||
95 | + for x in range(filter_w): | ||
96 | + x_max = x + stride*out_w | ||
97 | + col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride] | ||
98 | + | ||
99 | + col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N*out_h*out_w, -1) | ||
100 | + return col | ||
101 | + | ||
102 | + | ||
103 | +def col2im(col, input_shape, filter_h, filter_w, stride=1, pad=0): | ||
104 | + """ | ||
105 | + | ||
106 | + Parameters | ||
107 | + ---------- | ||
108 | + col : | ||
109 | + input_shape : 入力データの形状(例:(10, 1, 28, 28)) | ||
110 | + filter_h : | ||
111 | + filter_w | ||
112 | + stride | ||
113 | + pad | ||
114 | + | ||
115 | + Returns | ||
116 | + ------- | ||
117 | + | ||
118 | + """ | ||
119 | + N, C, H, W = input_shape | ||
120 | + out_h = (H + 2*pad - filter_h)//stride + 1 | ||
121 | + out_w = (W + 2*pad - filter_w)//stride + 1 | ||
122 | + col = col.reshape(N, out_h, out_w, C, filter_h, filter_w).transpose(0, 3, 4, 5, 1, 2) | ||
123 | + | ||
124 | + img = cp.zeros((N, C, H + 2*pad + stride - 1, W + 2*pad + stride - 1), dtype=np.float32) | ||
125 | + for y in range(filter_h): | ||
126 | + y_max = y + stride*out_h | ||
127 | + for x in range(filter_w): | ||
128 | + x_max = x + stride*out_w | ||
129 | + img[:, :, y:y_max:stride, x:x_max:stride] += col[:, :, y, x, :, :] | ||
130 | + | ||
131 | + return img[:, :, pad:H + pad, pad:W + pad] |
-
Please register or login to post a comment