tek4

Tái Cấu Trúc Vòng Lặp Huấn Luyện CNN

by - September. 21, 2021
Kiến thức
Machine Learning
Python
<p style="text-align: justify;"><em><span style="font-weight: 400;">Ch&agrave;o mừng c&aacute;c bạn quay trở lại với loạt b&agrave;i về </span><a href="https://tek4.vn/lap-trinh-neural-network-voi-pytorch-deep-learning-voi-pytorch/"><strong>Lập Tr&igrave;nh Neural Network Với Pytorch</strong></a><span style="font-weight: 400;">. </span><span style="font-weight: 400;">Trong b&agrave;i n&agrave;y, ch&uacute;ng ta sẽ thấy c&aacute;ch t&aacute;i cấu tr&uacute;c v&ograve;ng lặp huấn luyện để c&oacute; thể thử nghiệm với số lượng lớn c&aacute;c gi&aacute; trị si&ecirc;u tham số một c&aacute;ch dễ d&agrave;ng trong khi vẫn giữ cho v&ograve;ng lặp huấn luyện v&agrave; kết quả của ch&uacute;ng ta c&oacute; tổ chức.. Bắt đầu th&ocirc;i!</span></em></p> <h3 style="text-align: justify;"><img style="width: 100%;" src="http://tek4vn.2soft.top/public_files/tai-cau-truc-vong-lap-huan-luyen-png-1" alt="T&aacute;i Cấu Tr&uacute;c V&ograve;ng Lặp Huấn Luyện" /></h3> <p style="text-align: center;"><a href="https://tek4.vn/training-loop-run-builder-lap-trinh-neural-network-voi-pytorch/" target="_blank" rel="noopener">Xem th&ecirc;m b&agrave;i viết trước:&nbsp;Training Loop Run Builder</a></p> <h3 style="text-align: justify;"><strong>Cleaning v&ograve;ng lặp huấn luyện v&agrave; extracting classes</strong></h3> <p style="text-align: justify;"><span style="font-weight: 400;">Khi ch&uacute;ng ta rời khỏi v&ograve;ng huấn luyện của m&igrave;nh ở một v&agrave;i b&agrave;i trở lại đ&acirc;y, ch&uacute;ng ta đ&atilde; x&acirc;y dựng kh&aacute; nhiều chức năng cho ph&eacute;p thử nghiệm với nhiều tham số v&agrave; gi&aacute; trị kh&aacute;c nhau, đồng thời ch&uacute;ng ta cũng thực hiện c&aacute;c lệnh gọi cần thiết b&ecirc;n trong v&ograve;ng huấn luyện để nhận được kết quả v&agrave;o TensorBoard.</span></p> <p style="text-align: justify;"><span style="font-weight: 400;">Tất cả c&ocirc;ng việc n&agrave;y đ&atilde; gi&uacute;p &iacute;ch, nhưng v&ograve;ng huấn luyện của ch&uacute;ng ta hiện kh&aacute; rối. Trong b&agrave;i n&agrave;y, ch&uacute;ng ta sẽ </span><span style="font-weight: 400;">clean </span><span style="font-weight: 400;">v&ograve;ng lặp huấn luyện của m&igrave;nh v&agrave; tạo tiền đề cho nhiều thử nghiệm hơn bằng c&aacute;ch sử dụng lớp </span><em><span style="font-weight: 400;">RunBuilder</span></em><span style="font-weight: 400;"> m&agrave; ch&uacute;ng ta đ&atilde; x&acirc;y dựng lần trước v&agrave; bằng c&aacute;ch x&acirc;y dựng một lớp mới c&oacute; t&ecirc;n </span><em><span style="font-weight: 400;">RunManager</span></em><span style="font-weight: 400;">.&nbsp;&nbsp;</span></p> <p style="text-align: justify;">Mục ti&ecirc;u của ch&uacute;ng ta l&agrave; c&oacute; thể th&ecirc;m c&aacute;c tham số v&agrave; gi&aacute; trị để tất cả c&aacute;c gi&aacute; trị được kiểm tra hoặc thử trong nhiều lần chạy huấn luyện.</p> <p style="text-align: justify;">V&iacute; dụ, trong trường hợp n&agrave;y, ch&uacute;ng t&ocirc;i muốn sử dụng hai tham số, <em>lr</em> v&agrave; <em>batch_size,</em> v&agrave; đối với <em>batch_size,</em> ch&uacute;ng ta muốn thử hai gi&aacute; trị kh&aacute;c nhau.&nbsp;Điều n&agrave;y cho ch&uacute;ng ta tổng cộng hai lần huấn luyện.&nbsp;Cả hai lần chạy sẽ c&oacute; c&ugrave;ng tốc độ học tập trong khi batch size kh&aacute;c nhau.</p> <pre class="language-python"><code>params = OrderedDict( lr = [.01] ,batch_size = [1000, 2000] )</code></pre> <p style="text-align: justify;">Về kết quả, ch&uacute;ng ta c&oacute; thể xem v&agrave; c&oacute; thể so s&aacute;nh cả hai lần chạy.</p> <p style="text-align: justify;"><img style="width: 100%;" src="http://tek4vn.2soft.top/public_files/322-png" alt="322" /></p> <h4 class="sub-section-heading" style="text-align: justify;">Hai classes m&agrave; ch&uacute;ng ta sẽ x&acirc;y dựng</h4> <p style="text-align: justify;">Để l&agrave;m điều n&agrave;y, ch&uacute;ng ta cần x&acirc;y dựng hai lớp mới.&nbsp;Ch&uacute;ng ta đ&atilde; x&acirc;y dựng lớp đầu ti&ecirc;n c&oacute; t&ecirc;n <em>RunBuilder</em> trong b&agrave;i trước.&nbsp;N&oacute; được gọi như sau:</p> <pre class="language-python"><code>for run in RunBuilder.get_runs(params):</code></pre> <p style="text-align: justify;">B&acirc;y giờ, ch&uacute;ng ta cần x&acirc;y dựng lớp <em>RunManager</em>&nbsp;để cho ph&eacute;p quản l&yacute; từng lần chạy b&ecirc;n trong v&ograve;ng lặp chạy của ch&uacute;ng ta. Instance&nbsp;<em>RunManager</em> sẽ cho ph&eacute;p ch&uacute;ng ta r&uacute;t ra rất nhiều lệnh gọi TensorBoard v&agrave; cho ph&eacute;p th&ecirc;m h&agrave;m bổ sung.</p> <p style="text-align: justify;">Ch&uacute;ng ta sẽ thấy rằng khi số lượng tham số v&agrave; số lần chạy ng&agrave;y c&agrave;ng lớn, TensorBoard sẽ bắt đầu ph&acirc;n t&iacute;ch như một giải ph&aacute;p khả thi để xem x&eacute;t kết quả của ch&uacute;ng ta.</p> <p style="text-align: justify;"><em>RunManager</em> sẽ được gọi ở c&aacute;c giai đoạn kh&aacute;c nhau trong mỗi lần chạy.&nbsp;Ch&uacute;ng ta sẽ c&oacute; c&aacute;c cuộc gọi ở đầu v&agrave; cuối của cả giai đoạn run v&agrave; giai đoạn epoch.&nbsp;Ch&uacute;ng ta cũng sẽ c&oacute; c&aacute;c cuộc gọi để theo d&otilde;i sự mất m&aacute;t v&agrave; số lượng dự đo&aacute;n đ&uacute;ng trong mỗi epoch. Cuối c&ugrave;ng, ch&uacute;ng ta sẽ lưu kết quả chạy v&agrave;o đĩa.</p> <p style="text-align: justify;">H&atilde;y xem c&aacute;ch x&acirc;y dựng lớp <em>RunManager</em> n&agrave;y.</p> <h3 class="section-heading" style="text-align: justify;">X&acirc;y dựng <em>RunManger</em>&nbsp;để t&aacute;i cấu tr&uacute;c v&ograve;ng lặp huấn luyện</h3> <p style="text-align: justify;">H&atilde;y import c&aacute;c thư viện cần thiết:</p> <pre class="language-python"><code>import torch import torch.nn as nn import torch.optim as optim import torch.nn.functional as F import torchvision import torchvision.transforms as transforms from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriter from IPython.display import display, clear_output import pandas as pd import time import json from itertools import product from collections import namedtuple from collections import OrderedDict</code></pre> <p style="text-align: justify;">Đầu ti&ecirc;n, ch&uacute;ng ta khai b&aacute;o lớp bằng từ kh&oacute;a class.</p> <pre class="language-python"><code>class RunManager():</code></pre> <p style="text-align: justify;">Tiếp theo, ch&uacute;ng ta sẽ định nghĩa h&agrave;m tạo lớp.</p> <pre class="language-python"><code>def __init__(self): self.epoch_count = 0 self.epoch_loss = 0 self.epoch_num_correct = 0 self.epoch_start_time = None self.run_params = None self.run_count = 0 self.run_data = [] self.run_start_time = None self.network = None self.loader = None self.tb = None</code></pre> <p style="text-align: justify;">Hiện tại, ch&uacute;ng ta sẽ kh&ocirc;ng sử dụng đối số trong h&agrave;m khởi tạo v&agrave; sẽ chỉ x&aacute;c định một số thuộc t&iacute;nh cho ph&eacute;p theo d&otilde;i dữ liệu qua c&aacute;c lần chạy v&agrave; trong c&aacute;c epochs.</p> <p style="text-align: justify;">Ch&uacute;ng ta sẽ theo d&otilde;i những điều sau:</p> <ul style="text-align: justify;"> <li>Số&nbsp;epochs</li> <li>Sự mất m&aacute;t đang chạy trong một epoch</li> <li>Số dự đo&aacute;n đ&uacute;ng cho một epoch</li> <li>Thời gian bắt đầu của epoch</li> </ul> <p style="text-align: justify;">H&atilde;y nhớ rằng ch&uacute;ng ta đ&atilde; thấy lớp <em>RunManager</em> c&oacute; hai phương thức với epoch trong t&ecirc;n, đ&oacute; l&agrave;&nbsp;<em>begin_epoch()</em> v&agrave; <em>end_epoch().&nbsp;</em>Hai phương thức n&agrave;y sẽ cho ph&eacute;p ch&uacute;ng ta quản l&yacute; c&aacute;c gi&aacute; trị n&agrave;y trong suốt v&ograve;ng đời của epoch.</p> <p style="text-align: justify;">B&acirc;y giờ, tiếp theo ch&uacute;ng ta c&oacute; một số thuộc t&iacute;nh cho c&aacute;c lần chạy.&nbsp;Ch&uacute;ng ta c&oacute; một thuộc t&iacute;nh gọi l&agrave; <em>run_params.&nbsp;</em>Đ&acirc;y l&agrave; định nghĩa run về c&aacute;c tham số chạy.&nbsp;Gi&aacute; trị của n&oacute; sẽ l&agrave; một trong những lần chạy được trả về bởi lớp RunBuilder.</p> <p style="text-align: justify;">Tiếp theo, ch&uacute;ng ta c&oacute; c&aacute;c thuộc t&iacute;nh để theo d&otilde;i l&agrave; <em>run_count</em> v&agrave; <em>run_data.&nbsp;</em><em>Run_count</em> cung cấp cho ch&uacute;ng ta số lần chạy v&agrave; <em>run_data</em> l&agrave; danh s&aacute;ch sẽ sử dụng để theo d&otilde;i c&aacute;c gi&aacute; trị tham số v&agrave; kết quả của từng epoch cho mỗi lần chạy v&agrave; v&igrave; vậy ch&uacute;ng ta sẽ thấy rằng ch&uacute;ng ta th&ecirc;m một gi&aacute; trị v&agrave;o danh s&aacute;ch n&agrave;y cho mỗi epoch.&nbsp;Sau đ&oacute;, ch&uacute;ng ta c&oacute; thời gian bắt đầu chạy sẽ được sử dụng để t&iacute;nh thời lượng chạy.</p> <p style="text-align: justify;">Tiếp theo, ch&uacute;ng ta sẽ lưu mạng v&agrave; dataloader đang được sử dụng để chạy, cũng như <em>SummaryWriter</em> m&agrave; ch&uacute;ng ta c&oacute; thể sử dụng để lưu dữ liệu cho TensorBoard.</p> <h4 class="sub-section-heading" style="text-align: justify;">Code smells l&agrave; g&igrave;</h4> <p style="text-align: justify;">Code smell&nbsp;l&agrave; một thuật ngữ được sử dụng để m&ocirc; tả t&igrave;nh trạng về code trước mắt ch&uacute;ng ta dường như kh&ocirc;ng đ&uacute;ng.</p> <p style="text-align: justify;">Code smell&nbsp;kh&ocirc;ng c&oacute; nghĩa l&agrave; chắc chắn c&oacute; g&igrave; đ&oacute; sai. Code smell&nbsp;kh&ocirc;ng c&oacute; nghĩa l&agrave; code kh&ocirc;ng ch&iacute;nh x&aacute;c.&nbsp;N&oacute; chỉ c&oacute; nghĩa l&agrave; c&oacute; thể c&oacute; một c&aacute;ch kh&aacute;c tốt hơn.&nbsp;Trong trường hợp n&agrave;y, code smell thực tế l&agrave; ch&uacute;ng ta c&oacute; một số t&ecirc;n biến c&oacute; tiền tố.&nbsp;Việc sử dụng tiền tố ở đ&acirc;y chỉ ra rằng c&aacute;c biến bằng c&aacute;ch n&agrave;o đ&oacute; thuộc về nhau.</p> <p style="text-align: justify;">Bất cứ khi n&agrave;o ch&uacute;ng ta thấy điều n&agrave;y, ch&uacute;ng ta cần phải suy nghĩ về việc loại bỏ c&aacute;c tiền tố n&agrave;y.&nbsp;Dữ liệu thuộc về nhau n&ecirc;n ở c&ugrave;ng nhau.&nbsp;Điều n&agrave;y được thực hiện bằng c&aacute;ch đ&oacute;ng g&oacute;i dữ liệu b&ecirc;n trong một lớp.&nbsp;Rốt cuộc, nếu dữ liệu thuộc về nhau, ng&ocirc;n ngữ hướng đối tượng cho ch&uacute;ng ta khả năng diễn đạt thực tế n&agrave;y bằng c&aacute;ch sử dụng c&aacute;c lớp.</p> <h4 class="sub-section-heading" style="text-align: justify;">T&aacute;i cấu tr&uacute;c v&ograve;ng lặp bằng c&aacute;ch tr&iacute;ch xuất một lớp</h4> <p style="text-align: justify;">Hiện tại bạn c&oacute; thể để nguy&ecirc;n code n&agrave;y, nhưng sau n&agrave;y ch&uacute;ng ta c&oacute; thể muốn cấu tr&uacute;c lại code n&agrave;y bằng c&aacute;ch thực hiện những g&igrave; được gọi l&agrave; tr&iacute;ch xuất một lớp.&nbsp;Đ&acirc;y l&agrave; một kỹ thuật t&aacute;i cấu tr&uacute;c trong đ&oacute; ch&uacute;ng ta loại bỏ c&aacute;c tiền tố n&agrave;y v&agrave; tạo một lớp c&oacute; t&ecirc;n l&agrave; <em>Epoch,</em> c&oacute; c&aacute;c thuộc t&iacute;nh:&nbsp; <em>count, loss, num_correct</em> v&agrave; <em>start_time.</em></p> <pre class="language-python"><code>class Epoch(): def __init__(self): self.count = 0 self.loss = 0 self.num_correct = 0 self.start_time = None </code></pre> <p style="text-align: justify;">Sau đ&oacute;, ch&uacute;ng ta sẽ thay thế c&aacute;c biến lớp n&agrave;y bằng một instance của lớp Epoch.&nbsp;Ch&uacute;ng ta thậm ch&iacute; c&oacute; thể thay đổi biến đếm để c&oacute; t&ecirc;n trực quan hơn, như <em>number</em> hoặc <em>id.&nbsp;</em>L&yacute; do ch&uacute;ng ta c&oacute; thể bỏ điều n&agrave;y ngay b&acirc;y giờ l&agrave; v&igrave; t&aacute;i cấu tr&uacute;c l&agrave; một qu&aacute; tr&igrave;nh lặp đi lặp lại v&agrave; đ&acirc;y l&agrave; lần lặp đầu ti&ecirc;n của ch&uacute;ng ta.</p> <h4 class="sub-section-heading" style="text-align: justify;">Giải n&eacute;n c&aacute;c lớp tạo c&aacute;c layers trừu tượng</h4> <p style="text-align: justify;">Tr&ecirc;n thực tế, những g&igrave; ch&uacute;ng ta đang l&agrave;m b&acirc;y giờ bằng c&aacute;ch x&acirc;y dựng lớp n&agrave;y l&agrave; tr&iacute;ch xuất một lớp từ chương tr&igrave;nh v&ograve;ng lặp đ&agrave;o tạo ch&iacute;nh của ch&uacute;ng ta. Code smell&nbsp;m&agrave; ch&uacute;ng ta đang giải quyết l&agrave; thực tế v&ograve;ng lặp của ch&uacute;ng ta đang trở n&ecirc;n lộn xộn v&agrave; bắt đầu c&oacute; vẻ qu&aacute; phức tạp.</p> <p style="text-align: justify;">Khi ch&uacute;ng ta viết một chương tr&igrave;nh ch&iacute;nh v&agrave; sau đ&oacute; cấu tr&uacute;c lại n&oacute;, ch&uacute;ng ta c&oacute; thể nghĩ đến việc tạo ra c&aacute;c lớp trừu tượng l&agrave;m cho chương tr&igrave;nh ch&iacute;nh ng&agrave;y c&agrave;ng dễ đọc v&agrave; dễ hiểu hơn.&nbsp;Mỗi phần của chương tr&igrave;nh phải rất dễ hiểu theo đ&uacute;ng nghĩa của n&oacute;.</p> <p style="text-align: justify;">Theo c&aacute;ch lặp đi lặp lại, ch&uacute;ng ta c&oacute; thể nghĩ đến việc bắt đầu với một chương tr&igrave;nh duy nhất, sau đ&oacute; tr&iacute;ch xuất code tạo ra c&aacute;c layers s&acirc;u v&agrave; rộng hơn.&nbsp;Qu&aacute; tr&igrave;nh n&agrave;y c&oacute; thể được xem như một cấu tr&uacute;c giống như c&acirc;y ph&acirc;n nh&aacute;nh.</p> <h4 class="sub-section-heading" style="text-align: justify;">Bắt đầu chạy v&ograve;ng huấn luyện</h4> <p style="text-align: justify;">D&ugrave; sao, ch&uacute;ng ta h&atilde;y xem x&eacute;t phương thức đầu ti&ecirc;n của lớp n&agrave;y, n&oacute; tr&iacute;ch xuất code cần thiết để bắt đầu chạy.</p> <pre class="language-python"><code>def begin_run(self, run, network, loader): self.run_start_time = time.time() self.run_params = run self.run_count += 1 self.network = network self.loader = loader self.tb = SummaryWriter(comment=f'-{run}') images, labels = next(iter(self.loader)) grid = torchvision.utils.make_grid(images) self.tb.add_image('images', grid) self.tb.add_graph(self.network, images)</code></pre> <p style="text-align: justify;">Đầu ti&ecirc;n, ch&uacute;ng ta nắm bắt thời gian bắt đầu chạy.&nbsp;Sau đ&oacute;, ch&uacute;ng ta lưu c&aacute;c tham số chạy được truyền v&agrave;o v&agrave; tăng số lần chạy l&ecirc;n một. Ch&uacute;ng ta lưu mạng v&agrave; data loader của m&igrave;nh, tiếp theo khởi tạo một <em>SummaryWriter</em> cho TensorBoard.&nbsp;Lưu &yacute; c&aacute;ch ch&uacute;ng ta đang truyền <em>run</em> của m&igrave;nh l&agrave;m đối số comment.&nbsp;Điều n&agrave;y sẽ cho ph&eacute;p ch&uacute;ng ta x&aacute;c định lần chạy&nbsp;duy nhất b&ecirc;n trong TensorBoard.</p> <p style="text-align: justify;">Tiếp theo, ch&uacute;ng ta c&oacute; một số lệnh gọi TensorBoard đ&atilde; thực hiện trong v&ograve;ng đ&agrave;o tạo của m&igrave;nh trước đ&oacute;.&nbsp;C&aacute;c cuộc gọi n&agrave;y th&ecirc;m mạng v&agrave; một loạt h&igrave;nh ảnh v&agrave;o TensorBoard.</p> <p style="text-align: justify;">Khi ch&uacute;ng ta kết th&uacute;c một lần chạy, tất cả những g&igrave; ch&uacute;ng ta phải l&agrave;m l&agrave; đ&oacute;ng n&uacute;t điều khiển TensorBoard v&agrave; đặt số epoch về 0 để sẵn s&agrave;ng cho lần chạy tiếp theo.</p> <pre class="language-python"><code>def end_run(self): self.tb.close() self.epoch_count = 0</code></pre> <p style="text-align: justify;">Để bắt đầu một epoch, trước ti&ecirc;n ch&uacute;ng ta lưu thời gian bắt đầu.&nbsp;Sau đ&oacute;, ch&uacute;ng ta tăng <em>epoch_count</em> l&ecirc;n 1 v&agrave; đặt <em>epoch_loss</em> v&agrave; <em>epoch_number_correct</em> th&agrave;nh 0.</p> <pre class="language-python"><code>def begin_epoch(self): self.epoch_start_time = time.time() self.epoch_count += 1 self.epoch_loss = 0 self.epoch_num_correct = 0</code></pre> <p style="text-align: justify;">B&acirc;y giờ, h&atilde;y xem nơi m&agrave; phần lớn h&agrave;nh động xảy ra đang kết th&uacute;c một epoch.</p> <pre class="language-python"><code>def end_epoch(self): epoch_duration = time.time() - self.epoch_start_time run_duration = time.time() - self.run_start_time loss = self.epoch_loss / len(self.loader.dataset) accuracy = self.epoch_num_correct / len(self.loader.dataset) self.tb.add_scalar('Loss', loss, self.epoch_count) self.tb.add_scalar('Accuracy', accuracy, self.epoch_count) for name, param in self.network.named_parameters(): self.tb.add_histogram(name, param, self.epoch_count) self.tb.add_histogram(f'{name}.grad', param.grad, self.epoch_count) ...</code></pre> <p style="text-align: justify;">Ch&uacute;ng ta bắt đầu bằng c&aacute;ch t&iacute;nh to&aacute;n thời lượng epoch v&agrave; thời lượng chạy.&nbsp;V&igrave; ch&uacute;ng ta đang ở cuối một epoch, n&ecirc;n thời lượng của epoch l&agrave; cuối c&ugrave;ng, nhưng thời gian chạy ở đ&acirc;y đại diện cho thời gian chạy của lần chạy hiện tại.&nbsp;Gi&aacute; trị sẽ tiếp tục chạy cho đến khi qu&aacute; tr&igrave;nh chạy kết th&uacute;c.&nbsp;Tuy nhi&ecirc;n, ch&uacute;ng ta sẽ vẫn lưu n&oacute; theo từng epoch.</p> <p style="text-align: justify;">Tiếp theo, ch&uacute;ng ta t&iacute;nh to&aacute;n <em>epoch_loss</em> v&agrave; độ ch&iacute;nh x&aacute;c <em>(accuracy). C</em>h&uacute;ng ta l&agrave;m điều n&agrave;y li&ecirc;n quan đến k&iacute;ch thước của tập huấn luyện, điều n&agrave;y cung cấp cho ch&uacute;ng ta mức tổn thất trung b&igrave;nh tr&ecirc;n mỗi mẫu.&nbsp;Sau đ&oacute;, ch&uacute;ng ta chuyển cả hai gi&aacute; trị n&agrave;y cho TensorBoard.</p> <p style="text-align: justify;">Tiếp theo, ch&uacute;ng ta chuyển c&aacute;c trọng số v&agrave; gi&aacute; trị gradient của mạng tới TensorBoard như ch&uacute;ng t&ocirc;i đ&atilde; l&agrave;m trước đ&acirc;y.</p> <h4 class="sub-section-heading" style="text-align: justify;">Theo d&otilde;i hiệu suất v&ograve;ng lặp đ&agrave;o tạo</h4> <p style="text-align: justify;">B&acirc;y giờ ch&uacute;ng t&ocirc;i đ&atilde; sẵn s&agrave;ng cho những g&igrave; mới trong qu&aacute; tr&igrave;nh n&agrave;y.&nbsp;Đ&acirc;y l&agrave; phần m&agrave; ch&uacute;ng ta đang th&ecirc;m v&agrave;o để cung cấp th&ocirc;ng tin chi tiết bổ sung khi ch&uacute;ng ta chuẩn bị trước một số lượng lớn c&aacute;c lần chạy.&nbsp;Ch&uacute;ng ta sẽ tự lưu tất cả dữ liệu để c&oacute; thể ph&acirc;n t&iacute;ch dữ liệu đ&oacute; ở k&iacute;ch thước lớn hơn TensorBoard.</p> <pre class="language-python"><code>def end_epoch(self): ... results = OrderedDict() results["run"] = self.run_count results["epoch"] = self.epoch_count results['loss'] = loss results["accuracy"] = accuracy results['epoch duration'] = epoch_duration results['run duration'] = run_duration for k,v in self.run_params._asdict().items(): results[k] = v self.run_data.append(results) df = pd.DataFrame.from_dict(self.run_data, orient='columns') ...</code></pre> <p style="text-align: justify;">Ở đ&acirc;y, ch&uacute;ng ta đang x&acirc;y dựng một từ điển chứa c&aacute;c kh&oacute;a v&agrave; gi&aacute; trị m&agrave; ch&uacute;ng ta quan t&acirc;m cho qu&aacute; tr&igrave;nh chạy.&nbsp;Ch&uacute;ng ta th&ecirc;m <em>run_count, epoch_count,</em>&nbsp;<em>loss,</em>&nbsp;<em>accuracy, epoch_duration</em> v&agrave; <em>run_duration.&nbsp;</em></p> <p style="text-align: justify;">Sau đ&oacute;, ch&uacute;ng ta lặp lại c&aacute;c kh&oacute;a v&agrave; gi&aacute; trị b&ecirc;n trong c&aacute;c tham số chạy th&ecirc;m ch&uacute;ng v&agrave;o từ điển kết quả.&nbsp;Điều n&agrave;y sẽ cho ph&eacute;p ch&uacute;ng ta xem c&aacute;c tham số được li&ecirc;n kết với kết quả hiệu suất.</p> <p style="text-align: justify;">Cuối c&ugrave;ng, ch&uacute;ng t&ocirc;i nối kết quả v&agrave;o danh s&aacute;ch <em>run_data.&nbsp;</em></p> <p style="text-align: justify;">Khi dữ liệu được th&ecirc;m v&agrave;o danh s&aacute;ch, ch&uacute;ng ta biến danh s&aacute;ch dữ liệu th&agrave;nh pandas&nbsp;data frame để ch&uacute;ng ta c&oacute; thể c&oacute; đầu ra được định dạng.</p> <p style="text-align: justify;">Hai d&ograve;ng tiếp theo d&agrave;nh ri&ecirc;ng cho&nbsp;Jupyter notebook. Ch&uacute;ng ta x&oacute;a đầu ra hiện tại v&agrave; hiển thị khung dữ liệu mới.</p> <pre class="language-python"><code>clear_output(wait=True) display(df)</code></pre> <p style="text-align: justify;">Vậy l&agrave;&nbsp;kết th&uacute;c một epoch.&nbsp;&nbsp;Một điều bạn c&oacute; thể tự hỏi l&agrave; l&agrave;m thế n&agrave;o c&aacute;c gi&aacute; trị epoch_loss v&agrave; epoch_num_correct được theo d&otilde;i.&nbsp;Ch&uacute;ng ta sẽ c&oacute; hai phương ph&aacute;p ngay b&ecirc;n dưới cho việc đ&oacute;.</p> <pre class="language-python"><code>def track_loss(self, loss, batch): self.epoch_loss += loss.item() * batch[0].shape[0] def track_num_correct(self, preds, labels): self.epoch_num_correct += self.get_num_correct(preds, labels)</code></pre> <p style="text-align: justify;">Ch&uacute;ng ta c&oacute; một phương thức gọi l&agrave; <em>track_loss()</em> v&agrave; một phương thức gọi l&agrave; <em>track_num_correct().&nbsp;</em>C&aacute;c phương thức n&agrave;y được gọi b&ecirc;n trong v&ograve;ng lặp đ&agrave;o tạo sau mỗi batch.&nbsp;Sự mất m&aacute;t được chuyển v&agrave;o phương thức <em>track_loss()</em> v&agrave; c&aacute;c dự đo&aacute;n v&agrave; nh&atilde;n được chuyển v&agrave;o phương thức <em>track_num_correct().&nbsp;</em></p> <p style="text-align: justify;">Để t&iacute;nh to&aacute;n số lượng dự đo&aacute;n đ&uacute;ng, ch&uacute;ng ta đang sử dụng c&ugrave;ng một h&agrave;m <em>get_num_correct()</em> m&agrave; ch&uacute;ng ta đ&atilde; x&aacute;c định trong c&aacute;c b&agrave;i trước.&nbsp;Sự kh&aacute;c biệt ở đ&acirc;y l&agrave; h&agrave;m hiện được đ&oacute;ng g&oacute;i b&ecirc;n trong lớp RunManager của ch&uacute;ng ta.</p> <pre class="language-python"><code>def _get_num_correct(self, preds, labels): return preds.argmax(dim=1).eq(labels).sum().item()</code></pre> <p style="text-align: justify;">Cuối c&ugrave;ng, ch&uacute;ng ta c&oacute; một phương thức gọi l&agrave; <em>save()</em> lưu run_data ở hai định dạng, json v&agrave; csv.&nbsp;Đầu ra n&agrave;y được chuyển v&agrave;o ổ đĩa v&agrave; lu&ocirc;n sẵn s&agrave;ng cho c&aacute;c ứng dụng kh&aacute;c sử dụng.&nbsp;V&iacute; dụ: ch&uacute;ng t&ocirc;i c&oacute; thể mở tệp csv trong excel hoặc ch&uacute;ng t&ocirc;i thậm ch&iacute; c&oacute; thể x&acirc;y dựng TensorBoard của ri&ecirc;ng m&igrave;nh với dữ liệu tốt hơn.</p> <pre class="language-python"><code>def save(self, fileName): pd.DataFrame.from_dict( self.run_data, orient='columns' ).to_csv(f'{fileName}.csv') with open(f'{fileName}.json', 'w', encoding='utf-8') as f: json.dump(self.run_data, f, ensure_ascii=False, indent=4)</code></pre> <p style="text-align: justify;">B&acirc;y giờ, ch&uacute;ng ta c&oacute; thể sử dụng lớp RunManager n&agrave;y b&ecirc;n trong v&ograve;ng lặp đ&agrave;o tạo của m&igrave;nh.</p> <p style="text-align: justify;">Nếu ch&uacute;ng ta sử dụng c&aacute;c th&ocirc;ng số sau:</p> <pre class="language-python"><code>params = OrderedDict( lr = [.01] ,batch_size = [1000, 2000] ,shuffle = [True, False] )</code></pre> <p style="text-align: justify;">Đ&acirc;y l&agrave; những kết quả ch&uacute;ng ta nhận được:</p> <div class="table-responsive" style="text-align: justify;"> <table class="table table-sm table-hover" style="width: 92.6281%;"> <thead> <tr style="text-align: right;"> <th style="width: 3.87105%; text-align: center;">run</th> <th style="width: 6.85764%; text-align: center;">epoch</th> <th style="width: 5.70102%; text-align: center;">loss</th> <th style="width: 9.74803%; text-align: center;">accuracy</th> <th style="width: 17.2509%; text-align: center;">epoch duration</th> <th style="width: 14.2619%; text-align: center;">run duration</th> <th style="width: 4.40598%; text-align: center;">lr</th> <th style="width: 11.4724%; text-align: center;">batch_size</th> <th style="width: 7.79372%; text-align: center;">shuffle</th> </tr> </thead> <tbody> <tr> <td style="width: 3.87105%;">1</td> <td style="width: 6.85764%;">1</td> <td style="width: 5.70102%;">0.979</td> <td style="width: 9.74803%;">0.624</td> <td style="width: 17.2509%;">20.056</td> <td style="width: 14.2619%;">22.935</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">1000</td> <td style="width: 7.79372%;">True</td> </tr> <tr> <td style="width: 3.87105%;">1</td> <td style="width: 6.85764%;">2</td> <td style="width: 5.70102%;">0.514</td> <td style="width: 9.74803%;">0.805</td> <td style="width: 17.2509%;">19.786</td> <td style="width: 14.2619%;">43.141</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">1000</td> <td style="width: 7.79372%;">True</td> </tr> <tr> <td style="width: 3.87105%;">1</td> <td style="width: 6.85764%;">3</td> <td style="width: 5.70102%;">0.425</td> <td style="width: 9.74803%;">0.843</td> <td style="width: 17.2509%;">20.117</td> <td style="width: 14.2619%;">63.342</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">1000</td> <td style="width: 7.79372%;">True</td> </tr> <tr> <td style="width: 3.87105%;">1</td> <td style="width: 6.85764%;">4</td> <td style="width: 5.70102%;">0.378</td> <td style="width: 9.74803%;">0.861</td> <td style="width: 17.2509%;">19.556</td> <td style="width: 14.2619%;">82.969</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">1000</td> <td style="width: 7.79372%;">True</td> </tr> <tr> <td style="width: 3.87105%;">1</td> <td style="width: 6.85764%;">5</td> <td style="width: 5.70102%;">0.342</td> <td style="width: 9.74803%;">0.872</td> <td style="width: 17.2509%;">18.706</td> <td style="width: 14.2619%;">101.752</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">1000</td> <td style="width: 7.79372%;">True</td> </tr> <tr> <td style="width: 3.87105%;">2</td> <td style="width: 6.85764%;">1</td> <td style="width: 5.70102%;">0.965</td> <td style="width: 9.74803%;">0.632</td> <td style="width: 17.2509%;">18.846</td> <td style="width: 14.2619%;">19.390</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">1000</td> <td style="width: 7.79372%;">False</td> </tr> <tr> <td style="width: 3.87105%;">2</td> <td style="width: 6.85764%;">2</td> <td style="width: 5.70102%;">0.503</td> <td style="width: 9.74803%;">0.806</td> <td style="width: 17.2509%;">20.276</td> <td style="width: 14.2619%;">39.758</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">1000</td> <td style="width: 7.79372%;">False</td> </tr> <tr> <td style="width: 3.87105%;">2</td> <td style="width: 6.85764%;">3</td> <td style="width: 5.70102%;">0.409</td> <td style="width: 9.74803%;">0.849</td> <td style="width: 17.2509%;">19.741</td> <td style="width: 14.2619%;">59.579</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">1000</td> <td style="width: 7.79372%;">False</td> </tr> <tr> <td style="width: 3.87105%;">2</td> <td style="width: 6.85764%;">4</td> <td style="width: 5.70102%;">0.360</td> <td style="width: 9.74803%;">0.866</td> <td style="width: 17.2509%;">19.358</td> <td style="width: 14.2619%;">79.015</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">1000</td> <td style="width: 7.79372%;">False</td> </tr> <tr> <td style="width: 3.87105%;">2</td> <td style="width: 6.85764%;">5</td> <td style="width: 5.70102%;">0.330</td> <td style="width: 9.74803%;">0.877</td> <td style="width: 17.2509%;">19.523</td> <td style="width: 14.2619%;">98.616</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">1000</td> <td style="width: 7.79372%;">False</td> </tr> <tr> <td style="width: 3.87105%;">3</td> <td style="width: 6.85764%;">1</td> <td style="width: 5.70102%;">1.298</td> <td style="width: 9.74803%;">0.513</td> <td style="width: 17.2509%;">18.831</td> <td style="width: 14.2619%;">20.039</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">2000</td> <td style="width: 7.79372%;">True</td> </tr> <tr> <td style="width: 3.87105%;">3</td> <td style="width: 6.85764%;">2</td> <td style="width: 5.70102%;">0.665</td> <td style="width: 9.74803%;">0.745</td> <td style="width: 17.2509%;">18.872</td> <td style="width: 14.2619%;">38.988</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">2000</td> <td style="width: 7.79372%;">True</td> </tr> <tr> <td style="width: 3.87105%;">3</td> <td style="width: 6.85764%;">3</td> <td style="width: 5.70102%;">0.548</td> <td style="width: 9.74803%;">0.789</td> <td style="width: 17.2509%;">18.947</td> <td style="width: 14.2619%;">58.012</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">2000</td> <td style="width: 7.79372%;">True</td> </tr> <tr> <td style="width: 3.87105%;">3</td> <td style="width: 6.85764%;">4</td> <td style="width: 5.70102%;">0.485</td> <td style="width: 9.74803%;">0.819</td> <td style="width: 17.2509%;">19.325</td> <td style="width: 14.2619%;">77.416</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">2000</td> <td style="width: 7.79372%;">True</td> </tr> <tr> <td style="width: 3.87105%;">3</td> <td style="width: 6.85764%;">5</td> <td style="width: 5.70102%;">0.443</td> <td style="width: 9.74803%;">0.838</td> <td style="width: 17.2509%;">19.629</td> <td style="width: 14.2619%;">97.121</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">2000</td> <td style="width: 7.79372%;">True</td> </tr> <tr> <td style="width: 3.87105%;">4</td> <td style="width: 6.85764%;">1</td> <td style="width: 5.70102%;">1.305</td> <td style="width: 9.74803%;">0.497</td> <td style="width: 17.2509%;">19.242</td> <td style="width: 14.2619%;">20.465</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">2000</td> <td style="width: 7.79372%;">False</td> </tr> <tr> <td style="width: 3.87105%;">4</td> <td style="width: 6.85764%;">2</td> <td style="width: 5.70102%;">0.693</td> <td style="width: 9.74803%;">0.727</td> <td style="width: 17.2509%;">18.858</td> <td style="width: 14.2619%;">39.406</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">2000</td> <td style="width: 7.79372%;">False</td> </tr> <tr> <td style="width: 3.87105%;">4</td> <td style="width: 6.85764%;">3</td> <td style="width: 5.70102%;">0.572</td> <td style="width: 9.74803%;">0.777</td> <td style="width: 17.2509%;">18.839</td> <td style="width: 14.2619%;">58.321</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">2000</td> <td style="width: 7.79372%;">False</td> </tr> <tr> <td style="width: 3.87105%;">4</td> <td style="width: 6.85764%;">4</td> <td style="width: 5.70102%;">0.503</td> <td style="width: 9.74803%;">0.809</td> <td style="width: 17.2509%;">18.774</td> <td style="width: 14.2619%;">77.168</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">2000</td> <td style="width: 7.79372%;">False</td> </tr> <tr> <td style="width: 3.87105%;">4</td> <td style="width: 6.85764%;">5</td> <td style="width: 5.70102%;">0.462</td> <td style="width: 9.74803%;">0.831</td> <td style="width: 17.2509%;">19.028</td> <td style="width: 14.2619%;">96.274</td> <td style="width: 4.40598%;">0.01</td> <td style="width: 11.4724%;">2000</td> <td style="width: 7.79372%;">False</td> </tr> </tbody> </table> </div> <p style="text-align: right;"><a href="https://tek4.vn/tang-toc-qua-trinh-huan-luyen-lap-trinh-neural-network-voi-pytorch/" target="_blank" rel="noopener">B&agrave;i viết tiếp theo: Tăng Tốc Qu&aacute; Tr&igrave;nh Huấn Luyện</a></p> <p style="text-align: justify;">&nbsp;</p> <hr /> <p style="text-align: center;"><em><strong>Fanpage Facebook:</strong>&nbsp;<a href="https://www.facebook.com/tek4.vn/">TEK4.VN</a></em>&nbsp;</p> <p style="text-align: center;"><em><strong>Tham gia cộng đồng để chia sẻ, trao đổi v&agrave; thảo luận:</strong>&nbsp;<a href="https://www.facebook.com/groups/tek4.vn/">TEK4.VN - Học Lập Tr&igrave;nh Miễn Ph&iacute;</a></em></p>