Chào tất cả các bạn, trong bài viết này Nguyễn Văn Hiếu blog tiếp tục trình bày series về khóa học Tensorflow. Nếu bạn chưa nắm được thông tin tutorial này, bạn hãy xem bài viết giới thiệu trước tiên nhé. Bài đầu tiên sẽ mô tả chi tiết thông tin về khóa học này, các yêu cầu với người học, cài đặt môi trường và danh sách các bài học từ đầu đến cuối.
Bài học trước: Xây dựng mô hình linear regression sử dụng Tensorflow
Trong bài viết này, chúng ta sẽ cùng đi xây dựng một model logistic regression và cùng bàn luận về các vấn đề xung quanh.
Toàn bộ source code của bài này các bạn có thể xem tại đây
Sự khác nhau giữa linear model và logistic model
Có 3 sự khác nhau chính:
- Trong linear regression, dữ liệu được mô hình hóa/ biểu diễn bằng 1 đường thẳng. Còn logistic regression thì kết quả dự đoán sẽ là một số kết quả đi kèm theo xác suất.
- Mối quan hệ giữa các biến độc lập và biến phụ thuộc cần phải có trong mô hình linear regression, còn logistic regression thì không.
- Các biến độc lập có thể có mối quan hệ lẫn nhau trong linear regression, và nó không đúng trong logistic regression.
Bạn đọc tham khảo thêm tại bài viết này để có được cái nhìn tổng quát hơn
Xây dựng mô hình logistic regression trên tệp dữ liệu notMNIST
Đây là một bài tập trong khóa học cs20si về tensorflow. Bài toán cụ thể như sau:
Bộ dữ liệu là hình ảnh của 10 nhãn khác nhau. Các nhãn là các chữ cái từ A đến J. mỗi ảnh trong dữ liệu có kích thước 28 x 28 và đã biết trước nhãn. Mục tiêu của chúng ta là phải xây dựng 1 model logistic regression dựa trên tập dữ liệu này để có thể dự đoán nhãn của ảnh chưa biết.
Bộ dữ liệu này chưa được đưa về format giống như dữ liệu MNIST. Tuy nhiên, đã có sẵn script hỗ trợ bạn làm việc này. Và trong bài này, tôi đã làm sẵn cho các bạn. Nếu các bạn thích làm từ đầu, có thể follow hướng dẫn làm bài tập đầy đủ tại đây.
Bộ dữ liệu sẽ được chia ra làm 2 tập riêng biệt: train cho quá trình huấn luyện và test cho quá trình đánh giá mô hình. Trong đó mỗi điểm dữ liệu có kích thước là 1 x 784(chúng ta đơn giản hóa bằng cách chuyển ảnh 28×28 pixel = bằng vector có 784 chiều( = 28 * 28)), mỗi nhãn là 1 one-hot vector có kích thước 1 x 10(tại có 10 nhãn mà).
Giải thích thêm nhãn dạng one-hot vector
Ở đây bạn có 10 nhãn (A, B,C,… J) tương ứng 10 chữ cái. Ta có 1 vector tương ứng có 10 phần tử. Có duy nhất và luôn có1 phần tử có giá trị là 1 nếu đó là nhãn của điểm dữ liệu, các phần tử còn lại có giá trị là 0. Ví dụ:
- Nếu điểm dữ liệu của bạn có nhãn là A thì vector nhãn là (1,0,0,0,0,0,0,0,0,0).
- Nếu điểm dữ liệu của bạn có nhãn là C thì vector nhãn là (0,0,1,0,0,0,0,0,0,0).
Bây giờ, chúng ta cần thực hiện từng bước của 1 chương trình TF.
Xây dựng graph cho bài toán logistic regression
Đầu tiên, ta sẽ định nghĩa một số tham số cho model
0
1
2
3
4
|
learning_rate = 0.01 # Tốc độ học
batch_size = 128 # số mẫu dữ liệu được lấy ra cho mỗi lần học
n_epochs = 30 # 1 epoch là một lần học đầy đủ trên dữ liệu huấn luyện
|
Bạn có thể hiểu, 1 batch_size là số mẫu dữ liệu lấy ra cho 1 lần học. Còn 1 epoch là hoàn thành việc học trên toàn bộ dữ liệu huấn luyện. Chúng ta sẽ để cho model học đi học lại mẫu dữ liệu huấn luyện 30 lần.
Đọc dữ liệu huấn luyện
0
1
2
3
|
X_train, X_validation, X_test = utils.read_mnist('notMnist')
X_batch, Y_batch = utils.next_batch(batch_size, X_train)
|
Các hàm
read_mnist
và next_batch
các bạn có thể xem tại file utils đã có sẵn. Các hàm này chỉ là code python thông thường nên tôi sẽ không trình bày. next_batch
trong trường hợp này là lấy ngẫu nhiên batch_size
mẫu dữ liệu ra cho mỗi lần học.X_train
, X_validation
, X_test
là các phần của dữ liệu được chia sẵn. Các phần dữ liệu này có len = 2
có định dạng như sau:X_train[0] là 1 tensor chứa các tensor dữ liệu của chúng ta. Tức là mỗi tensor trong X_train[0] tương ứng là một ma trận 1 x 784.
X_train[1] là 1 tensor chứa các tensor nhãn tương ứng của X_train[0]. Tức là mỗi tensor trong X_test[0] tương ứng là ma trận nhãn có kích thước 1 x 10.
Dữ liệu ảnh tại X_train[0][0] có nhãn là X_train[1][0], X_train[0][1] có nhãn là X_train[1][1], ...
X_test
và X_validation
tương tự. Và trong bài này chúng ta không sử dụng X_validation.
Tiếp theo, chúng ta sẽ cần tạo 2 placeholder để truyền input(image) và nhãn(chữ cái nào) vào khi thực thi model trong session.
0
1
2
3
|
X = tf.placeholder(tf.float32, [None, 784], name='image')
Y = tf.placeholder(tf.int32, [None, 10], name='label')
|
Tại sao X có shape là [None, 784]? Do dữ liệu ảnh của chúng ta là 28 x 28 được biểu diễn thành 1 vector 1 x 784. Nhưng tôi muốn truyền vào số điểm dữ liệu tùy ý khi sử dụng, do đó tôi để là None. Trong bài này, khi huấn luyện X sẽ có shape = [batch_size, 784], còn khi test thì là [test_size, 784].
Còn shape của Y thì sao? Do mỗi nhãn là một onehot vector có kích thước 1 x 10(1 cho nhãn của nó và 0 cho các nhãn còn lại) và ta muốn Y cũng nhận vào số điểm dữ liệu tùy ý nên để None.
Vẫn là 2 biến cần được tối ưu trong quá trình huấn luyện: weights và bias. w sẽ được khởi tạo giá trị ngẫu nhiên, còn b được khởi tạo 0.
0
1
2
3
|
w = tf.get_variable(name='weights', shape=(784, 10), initializer=tf.random_normal_initializer())
b = tf.get_variable(name='bias', shape=(1, 10), initializer=tf.zeros_initializer())
|
Kích thước của w phụ thuộc vào X và Y, do Y = tích của 2 ma trận
tf.matmul(X, w)
. Còn kích thước của b thì phụ thuộc vào b. Cái này liên quan tới điều kiện 2 ma trận có thể thực hiện phép nhân , cộng.
Và model của chúng ta sẽ có công thức như sau:
0
1
2
|
logits = tf.matmul(X, w) + b
|
Định nghĩa hàm mất mát
0
1
2
3
|
entropy = tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=Y, name='loss')
loss = tf.reduce_mean(entropy) # computes the mean over all the examples in the batch
|
Trong bài này, chúng ta không dùng bình phương chênh lệch nữa mà sẽ dùng độ đo cross entropy. Lý do là cross entropy được sử dụng rộng rãi khi tính khoảng cách giữa hai phân phối xác suất. Nó thể hiện sự mất mát rõ ràng hơn khi kết quả dự đoán xa với thực tế so với bình phương sai lệch.
Bạn đọc có thể đọc bài viết này để hiểu rõ hơn về cross entropy.
Tiếp đến, ta thực hiện tối ưu hàm loss sử dụng AdamOptimizer được định nghĩa trong thư viện TF.
0
1
2
|
optimizer = tf.train.AdamOptimizer(learning_rate).minimize(loss)
|
Để có thể đánh giá trực quan hơn, ngoài việc xem giá trị hàm loss ta còn cần sử dụng một bộ dữ liệu test chưa được dùng trong quá trình huấn luyện để đánh giá mô hình.
Do mô hình này trả về nhiều hơn 1 kết quả. Chúng ta cần dùng
softmax
tất cả các kết quả về phân bố xác suất sao cho tổng của tất cả các kết quả là 100%. Việc của chúng ta là lấy kết quả có xác suất cao nhất đi so sánh với giá trị thực.
0
1
2
3
4
|
preds = tf.nn.softmax(logits) # đưa các kết quả dự đoán về xác suất
correct_preds = tf.equal(tf.argmax(preds, 1), tf.argmax(Y, 1))
accuracy = tf.reduce_sum(tf.cast(correct_preds, tf.float32))
|
Thực thi tính toán trên Graph qua Session
Vậy là đã xong bước định nghĩa graph. Đây là đoạn code triển khai thực thi tính toán để tối ưu hàm loss.
n_batches
là số lần lấy ra batch_size
cần để học hết 1 lượt của dữ liệu huấn luyện nếu lấy lần lượt. Tuy nhiên, trong code này, tôi sử dụng lấy ngẫu nhiên nên ko đảm bảo sau n_batches
sẽ đi hết toàn bộ dữ liệu. Nhưng việc lấy ngẫu nhiên giúp cho việc học của model được bao quát hơn.
Công việc của chúng ta là chạy op
optimizer
để nó thực hiện tối ưu hàm loss.
Tại mỗi lần học, chúng ta sẽ truyền vào 1 batch_size X, Y thông qua feed_dict. Đồng thời tiến hành tính toán và theo dõi sự thay đổi của giá trị loss.
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
with tf.Session() as sess:
start_time = time.time()
sess.run(tf.global_variables_initializer())
n_batches = int(len(X_train[0]) / batch_size)
# train the model n_epochs times
for i in range(n_epochs):
total_loss = 0
for j in range(n_batches):
X_batch, Y_batch = utils.next_batch(batch_size, X_train)
_, loss_batch = sess.run([optimizer, loss], {X: X_batch, Y: Y_batch})
total_loss += loss_batch
print('Average loss epoch {0}: {1}'.format(i, total_loss / n_batches))
|
Đây là sự thay đổi giá trị trung bình của sự mất mát ở 10 epoch đầu tiên:
0
1
2
3
4
5
6
7
8
9
10
11
12
|
Average loss epoch 0: 5.9370930057305555
Average loss epoch 1: 2.0318509072829514
Average loss epoch 2: 1.6000614288525703
Average loss epoch 3: 1.379857837389677
Average loss epoch 4: 1.2446619027700179
Average loss epoch 5: 1.155699616823441
Average loss epoch 6: 1.0422907013159532
Average loss epoch 7: 0.9455259285676174
Average loss epoch 8: 0.9020888690765088
Average loss epoch 9: 0.7838104967123423
Average loss epoch 10: 0.8283079056403576
|
Sau khi tối ưu hàm loss xong, chúng ta có thể kiểm tra độ chính xác trên tập dữ liệu test. Lưu ý: Vẫn thực thi trong session
0
1
2
3
|
_accuracy = sess.run(accuracy, {X: X_test[0], Y: X_test[1]})
print('Accuracy {0}'.format(_accuracy / len(X_test[0]) * 100), '%')
|
Nếu bạn để ý trong phần train feed_dict tôi truyền vào cho X và Y đều là
batch_size
điểm dữ liệu. Nhưng do chúng ta để shape của nó là tùy ý, bây giờ tôi có thể truyền vào cả tập test.
Và độ chính xác model cho ra là: 89.47%
Chú ý: Do model này lấy batch bằng cách lấy ngẫu nhiên, nên có thể bạn chạy sẽ cho độ chính xác không giống tôi. Nhưng sẽ không chênh lệch nhiều.
Biểu diễn của model này trên tensorboard.
Nếu các bạn học đến bài này, các bạn hoàn toàn có thể bắt đầu tìm và đhttps://nguyenvanhieu.vn/xay-dung-mo-hinh-neural-network/ọc hiểu các source code triển khai các mô hình học máy CNN, RNN,… phổ biến trên github cũng như bắt đầu tự triển khai các model của các bài báo học thuật. Chúc các bạn thành công!
Blogger Comment
Facebook Comment