tek4

Callable Neural Networks Trong Pytorch

by - September. 21, 2021
Kiến thức
Machine Learning
Python
<p style="text-align: justify;"><em>Ch&agrave;o mừng c&aacute;c bạn đến với b&agrave;i viết thứ 19 trong loạt b&agrave;i về&nbsp;<strong><a href="https://tek4.vn/lap-trinh-neural-network-voi-pytorch-deep-learning-voi-pytorch/" target="_blank" rel="noopener">Lập Tr&igrave;nh Neural Network Với Pytorch</a>.&nbsp;</strong>Trong phần n&agrave;y, ch&uacute;ng ta sẽ t&igrave;m hiểu về c&aacute;ch gọi c&aacute;c module mạng neural của PyTorch (Callable Neural Networks), điều n&agrave;y c&oacute; nghĩa l&agrave; g&igrave;? Bắt đầu th&ocirc;i!</em></p> <p style="text-align: justify;"><img style="width: 100%;" src="http://tek4vn.2soft.top/public_files/callable-neural-networks-png-1" alt="Callable Neural Networks" /></p> <h3 class="section-heading" style="text-align: justify;">C&aacute;ch layer tuyến t&iacute;nh hoạt động</h3> <p style="text-align: justify;">Khi c&aacute;c đặc trưng đầu v&agrave;o được nhận bởi một layer tuyến t&iacute;nh, ch&uacute;ng sẽ được nhận dưới dạng tensor 1 chiều phẳng v&agrave; sau đ&oacute; được nh&acirc;n với ma trận trọng số.&nbsp;Ph&eacute;p nh&acirc;n ma trận n&agrave;y tạo ra c&aacute;c đặc trưng đầu ra.</p> <p style="text-align: justify;"><img style="width: 100%;" src="http://tek4vn.2soft.top/public_files/matrix-multiplication-png" alt="matrix-multiplication" /></p> <p style="text-align: justify;">H&atilde;y xem một v&iacute; dụ về điều n&agrave;y trong code.</p> <h4 class="sub-section-heading" style="text-align: justify;">Chuyển Đổi Bằng Ma Trận</h4> <pre class="language-python"><code>in_features = torch.tensor([1,2,3,4], dtype=torch.float32) weight_matrix = torch.tensor([ [1,2,3,4], [2,3,4,5], [3,4,5,6] ], dtype=torch.float32) &gt; weight_matrix.matmul(in_features) tensor([30., 40., 50.])</code></pre> <p style="text-align: justify;">Ở đ&acirc;y, ch&uacute;ng ta đ&atilde; tạo ra một tensor 1 chiều được gọi l&agrave; <em>in_features.</em>&nbsp;Ch&uacute;ng ta cũng đ&atilde; tạo ra một ma trận trọng số <em>(<span class="pln">weight_matrix</span>)</em>&nbsp;l&agrave; một tensor 2 chiều.&nbsp;Sau đ&oacute;, ch&uacute;ng ta đ&atilde; sử dụng h&agrave;m <em>matmul()</em> để tạo sẵn ph&eacute;p to&aacute;n nh&acirc;n ma trận tạo ra tensor 1 chiều.</p> <p style="text-align: justify;">N&oacute;i chung, ma trận trọng số x&aacute;c định một h&agrave;m tuyến t&iacute;nh &aacute;nh xạ một tensor 1 chiều c&oacute; bốn phần tử th&agrave;nh tensor 1 chiều c&oacute; ba phần tử.&nbsp;Ch&uacute;ng ta c&oacute; thể coi h&agrave;m n&agrave;y như một &aacute;nh xạ từ kh&ocirc;ng gian Euclid 4 chiều sang kh&ocirc;ng gian Euclid 3 chiều.</p> <p style="text-align: justify;">Đ&acirc;y cũng l&agrave; c&aacute;ch c&aacute;c layers tuyến t&iacute;nh hoạt động.&nbsp;Ch&uacute;ng &aacute;nh xạ một kh&ocirc;ng gian <em>in_feature</em> th&agrave;nh một kh&ocirc;ng gian <em>out_feature</em> bằng c&aacute;ch sử dụng ma trận trọng số.</p> <h4 class="sub-section-heading" style="text-align: justify;">Transform bằng c&aacute;ch sử dụng một linear layer&nbsp;PyTorch</h4> <p style="text-align: justify;">H&atilde;y xem l&agrave;m thế n&agrave;o để tạo ra một lớp tuyến t&iacute;nh PyTorch:</p> <pre><code class="python">fc = nn.Linear(in_features=4, out_features=3, bias=False)</code></pre> <p style="text-align: justify;">Ch&uacute;ng ta đ&atilde; định nghĩa một layer tuyến t&iacute;nh chấp nhận 4 đặc trưng v&agrave; biến ch&uacute;ng th&agrave;nh 3 đặc trưng, v&igrave; vậy ch&uacute;ng ta chuyển từ kh&ocirc;ng gian 4 chiều sang kh&ocirc;ng gian 3 chiều.&nbsp;Ch&uacute;ng ta biết rằng một ma trận trọng số được sử dụng để định dạng trước hoạt động n&agrave;y, nhưng ma trận trọng số trong v&iacute; dụ n&agrave;y ở đ&acirc;u?</p> <p style="text-align: justify;">Ch&uacute;ng ta sẽ cho rằng ma trận trọng số nằm b&ecirc;n trong lớp PyTorch <em>LinearLayer</em> v&agrave; được tạo bởi PyTorch.&nbsp;Lớp PyTorch <em>LinearLayer</em> sử dụng c&aacute;c số 4 v&agrave; 3 được truyền đến h&agrave;m tạo để tạo ma trận trọng số 3 x 4.&nbsp;H&atilde;y x&aacute;c minh điều n&agrave;y bằng c&aacute;ch xem qua m&atilde; nguồn PyTorch.</p> <pre class="language-python"><code># torch/nn/modules/linear.py (version 1.0.1) def __init__(self, in_features, out_features, bias=True): super(Linear, self).__init__() self.in_features = in_features self.out_features = out_features self.weight = Parameter(torch.Tensor(out_features, in_features)) if bias: self.bias = Parameter(torch.Tensor(out_features)) else: self.register_parameter('bias', None) self.reset_parameters()</code></pre> <p style="text-align: justify;">Như ch&uacute;ng ta đ&atilde; biết, khi ch&uacute;ng ta nh&acirc;n ma trận 3 x 4 với ma trận 4 x 1, kết quả l&agrave; ma trận 3 x 1.&nbsp;Đ&acirc;y l&agrave; l&yacute; do tại sao PyTorch x&acirc;y dựng ma trận trọng số theo c&aacute;ch n&agrave;y.&nbsp;Đ&acirc;y l&agrave; c&aacute;c quy tắc đại số tuyến t&iacute;nh của ph&eacute;p nh&acirc;n ma trận.</p> <p style="text-align: justify;">H&atilde;y xem c&aacute;ch ch&uacute;ng ta c&oacute; thể gọi layer của m&igrave;nh bằng c&aacute;ch truyền v&agrave;o tensor <em>in_features.</em></p> <pre class="language-python"><code>&gt; fc(in_features) tensor([-0.8877, 1.4250, 0.8370], grad_fn=)</code></pre> <p style="text-align: justify;">Ch&uacute;ng ta c&oacute; thể gọi đối tượng như thế n&agrave;y v&igrave; c&aacute;c module mạng neural PyTorch l&agrave; c&aacute;c đối tượng Python <a href="https://en.wikipedia.org/wiki/Callable_object" target="_blank" rel="noopener">c&oacute; thể gọi được</a>.&nbsp;Ch&uacute;ng ta sẽ xem x&eacute;t chi tiết quan trọng n&agrave;y kỹ hơn sau, nhưng trước ti&ecirc;n, h&atilde;y kiểm tra kết quả n&agrave;y.&nbsp;Ch&uacute;ng ta đ&atilde; nhận được một tensor 1 chiều với ba phần tử.&nbsp;Tuy nhi&ecirc;n, c&aacute;c gi&aacute; trị kh&aacute;c nhau đ&atilde; được tạo ra.</p> <p style="text-align: justify;">Điều n&agrave;y l&agrave; do PyTorch tạo ra một ma trận trọng số v&agrave; khởi tạo n&oacute; với c&aacute;c gi&aacute; trị ngẫu nhi&ecirc;n. C&oacute; nghĩa l&agrave; c&aacute;c h&agrave;m tuyến t&iacute;nh từ hai v&iacute; dụ l&agrave; kh&aacute;c nhau, v&igrave; vậy ch&uacute;ng ta đang sử dụng h&agrave;m kh&aacute;c nhau để tạo c&aacute;c đầu ra n&agrave;y.</p> <p style="text-align: justify;">H&atilde;y nhớ c&aacute;c gi&aacute; trị b&ecirc;n trong ma trận trọng số x&aacute;c định h&agrave;m tuyến t&iacute;nh.&nbsp;Điều n&agrave;y cho thấy &aacute;nh xạ của mạng thay đổi như thế n&agrave;o khi c&aacute;c trọng số được cập nhật trong qu&aacute; tr&igrave;nh đ&agrave;o tạo.</p> <p style="text-align: justify;">H&atilde;y thiết lập r&otilde; r&agrave;ng ma trận trọng số của layer tuyến t&iacute;nh giống như ma trận ch&uacute;ng ta đ&atilde; sử dụng trong v&iacute; dụ kh&aacute;c.</p> <pre class="language-python"><code>fc.weight = nn.Parameter(weight_matrix) </code></pre> <p style="text-align: justify;">Trọng số module PyTorch cần phải l&agrave; tham số.&nbsp;Đ&acirc;y l&agrave; l&yacute; do tại sao ch&uacute;ng ta g&oacute;i tensor ma trận trọng số b&ecirc;n trong một lớp tham số.&nbsp;B&acirc;y giờ ch&uacute;ng ta h&atilde;y xem c&aacute;ch lớp n&agrave;y biến đổi đầu v&agrave;o bằng c&aacute;ch sử dụng ma trận trọng số mới. Hy vọng sẽ thấy kết quả tương tự như trong v&iacute; dụ trước của ch&uacute;ng ta:</p> <pre class="language-python"><code>&gt; fc(in_features) tensor([30.0261, 40.1404, 49.7643], grad_fn=)</code></pre> <p style="text-align: justify;">Lần n&agrave;y ch&uacute;ng ta đang tiến gần hơn đến c&aacute;c gi&aacute; trị 30, 40 v&agrave; 50. Điều n&agrave;y kh&ocirc;ng ch&iacute;nh x&aacute;c bởi v&igrave; layer tuyến t&iacute;nh đang th&ecirc;m một tensor bias v&agrave;o đầu ra.&nbsp;Xem điều g&igrave; sẽ xảy ra khi ch&uacute;ng ta tắt chế độ bias.&nbsp;Ch&uacute;ng ta thực hiện điều n&agrave;y bằng c&aacute;ch chuyển một cờ False cho h&agrave;m tạo.</p> <pre class="language-python"><code>fc = nn.Linear(in_features=4, out_features=3, bias=False) fc.weight = nn.Parameter(weight_matrix) &gt; fc(in_features) tensor([30., 40., 50.], grad_fn=)</code></pre> <p style="text-align: justify;">B&acirc;y giờ ch&uacute;ng ta c&oacute; một kết quả ch&iacute;nh x&aacute;c. Đ&acirc;y l&agrave; c&aacute;ch c&aacute;c layer tuyến t&iacute;nh hoạt động.</p> <h4 class="sub-section-heading" style="text-align: justify;">K&yacute; hiệu to&aacute;n học của ph&eacute;p linear transformation</h4> <p style="text-align: justify;">Đ&ocirc;i khi ch&uacute;ng ta sẽ thấy hoạt động của layer tuyến t&iacute;nh được gọi l&agrave;:</p> <p style="text-align: justify;">y=Ax+b</p> <p style="text-align: justify;">Trong phương tr&igrave;nh n&agrave;y, ch&uacute;ng ta c&oacute; như sau:</p> <table class=" aligncenter" style="width: 66.3462%;"> <tbody> <tr> <td style="text-align: center; width: 29.6553%;"><strong>Variable</strong></td> <td style="text-align: center; width: 59.6076%;"><strong>Định nghĩa</strong></td> </tr> <tr> <td style="width: 29.6553%; text-align: center;">A</td> <td style="width: 59.6076%;">Tensor ma trận trọng số</td> </tr> <tr> <td style="width: 29.6553%; text-align: center;">x</td> <td style="width: 59.6076%;">Đầu v&agrave;o tensor</td> </tr> <tr> <td style="width: 29.6553%; text-align: center;">b</td> <td style="width: 59.6076%;">Tensor bias</td> </tr> <tr> <td style="width: 29.6553%; text-align: center;">y</td> <td style="width: 59.6076%;">Đầu ra tensor</td> </tr> </tbody> </table> <p style="text-align: justify;">Lưu &yacute; rằng điều n&agrave;y tương tự như phương tr&igrave;nh cho một đường thẳng:</p> <p style="text-align: justify;">y=mx+b</p> <h3 class="section-heading" style="text-align: justify;">Callable layers v&agrave; mạng neural</h3> <p style="text-align: justify;">Trước đ&acirc;y ch&uacute;ng t&ocirc;i đ&atilde; chỉ ra rằng thật kỳ lạ khi ch&uacute;ng ta gọi instance object layer như thể n&oacute; l&agrave; một h&agrave;m.</p> <pre class="language-python"><code>&gt; fc(in_features) tensor([30.0261, 40.1404, 49.7643], grad_fn=)</code></pre> <p style="text-align: justify;">Điều l&agrave;m cho điều n&agrave;y c&oacute; thể xảy ra l&agrave; c&aacute;c lớp module PyTorch triển khai một h&agrave;m Python đặc biệt kh&aacute;c được gọi l&agrave; <em>__call __().&nbsp;</em>Nếu một lớp thực hiện phương thức <em>__call __(),</em> phương thức gọi đặc biệt sẽ được gọi bất cứ l&uacute;c n&agrave;o đối tượng được gọi.</p> <p style="text-align: justify;">Thực tế đ&acirc;y l&agrave; một kh&aacute;i niệm quan trọng của PyTorch v&igrave; c&aacute;ch phương thức __call <em>__()</em> tương t&aacute;c với phương thức <em>forward()</em> cho c&aacute;c layer v&agrave; mạng của ch&uacute;ng ta.</p> <p style="text-align: justify;">Thay v&igrave; gọi phương thức <em>forward()</em> trực tiếp, ch&uacute;ng ta gọi đối tượng instance.&nbsp;Sau khi object instance được gọi, phương thức <em>__call __()</em> được gọi b&ecirc;n dưới, v&agrave; lần lượt <em>__call __()</em> gọi phương thức <em>forward().&nbsp;</em>Điều n&agrave;y &aacute;p dụng cho tất cả c&aacute;c module mạng neural PyTorch, cụ thể l&agrave; mạng v&agrave; layers.</p> <p style="text-align: justify;">H&atilde;y xem điều n&agrave;y trong m&atilde; nguồn PyTorch:</p> <pre class="language-python"><code># torch/nn/modules/module.py (version 1.0.1) def __call__(self, *input, **kwargs): for hook in self._forward_pre_hooks.values(): hook(self, input) if torch._C._get_tracing_state(): result = self._slow_forward(*input, **kwargs) else: result = self.forward(*input, **kwargs) for hook in self._forward_hooks.values(): hook_result = hook(self, input, result) if hook_result is not None: raise RuntimeError( "forward hooks should never return any values, but '{}'" "didn't return None".format(hook)) if len(self._backward_hooks) &gt; 0: var = result while not isinstance(var, torch.Tensor): if isinstance(var, dict): var = next((v for v in var.values() if isinstance(v, torch.Tensor))) else: var = var[0] grad_fn = var.grad_fn if grad_fn is not None: for hook in self._backward_hooks.values(): wrapper = functools.partial(hook, self) functools.update_wrapper(wrapper, hook) grad_fn.register_hook(wrapper) return result</code></pre> <p style="text-align: justify;">Code&nbsp;bổ sung m&agrave; PyTorch chạy b&ecirc;n trong phương thức <em>__call __()</em> l&agrave; l&yacute; do tại sao ch&uacute;ng ta kh&ocirc;ng bao giờ gọi phương thức <em>forward()</em> trực tiếp.&nbsp;Nếu ch&uacute;ng ta gọi trực tiếp, m&atilde; PyTorch bổ sung sẽ kh&ocirc;ng được thực thi.&nbsp;Do đ&oacute;, bất cứ khi n&agrave;o ch&uacute;ng ta muốn gọi phương thức <em>forward()</em> của m&igrave;nh, ch&uacute;ng ta sẽ gọi đối tượng instance.</p> <p style="text-align: justify;">Điều n&agrave;y &aacute;p dụng cho cả layer v&agrave; mạng v&igrave; ch&uacute;ng đều l&agrave; module mạng neural PyTorch.</p> <p style="text-align: justify;">B&acirc;y giờ ch&uacute;ng ta đ&atilde; sẵn s&agrave;ng triển khai phương thức <em>forward()</em> của mạng.&nbsp;Hẹn gặp lại bạn trong phần tiếp theo!</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>