5.5. Đọc/Ghi tệp¶
Đến nay, ta đã thảo luận về cách xử lý dữ liệu và cách xây dựng, huấn luyện, kiểm tra những mô hình học sâu. Tuy nhiên, có thể đến một lúc nào đó ta sẽ cảm thấy hài lòng với những gì thu được và muốn lưu lại kết quả để sau này sử dụng trong những bối cảnh khác nhau (thậm chí có thể đưa ra dự đoán khi triển khai). Ngoài ra, khi vận hành một quá trình huấn luyện dài hơi, cách tốt nhất là lưu kết quả trung gian một cách định kỳ (điểm kiểm tra) nhằm đảm bảo rằng ta sẽ không mất kết quả tính toán sau nhiều ngày nếu chẳng may ta vấp phải dây nguồn của máy chủ. Vì vậy, đã đến lúc chúng ta học cách đọc và lưu trữ đồng thời các vector trọng số riêng lẻ cùng toàn bộ các mô hình. Mục này sẽ giải quyết cả hai vấn đề trên.
5.5.1. Đọc và Lưu các ndarray
¶
Đối với ndarray
riêng lẻ, ta có thể sử dụng trực tiếp các hàm
load
và save
để đọc và ghi tương ứng. Cả hai hàm đều yêu cầu ta
cung cấp tên, và hàm save
yêu cầu đầu vào với biến đã được lưu.
from mxnet import np, npx
from mxnet.gluon import nn
npx.set_np()
x = np.arange(4)
npx.save('x-file', x)
Bây giờ, chúng ta có thể đọc lại dữ liệu từ các tệp được lưu vào trong bộ nhớ.
x2 = npx.load('x-file')
x2
[array([0., 1., 2., 3.])]
MXNet đồng thời cho phép ta lưu một danh sách các ndarray
và đọc lại
chúng vào trong bộ nhớ.
y = np.zeros(4)
npx.save('x-files', [x, y])
x2, y2 = npx.load('x-files')
(x2, y2)
(array([0., 1., 2., 3.]), array([0., 0., 0., 0.]))
Ta còn có thể ghi và đọc một từ điển ánh xạ từ một chuỗi sang một
ndarray
. Điều này khá là thuận tiện khi chúng ta muốn đọc hoặc ghi
tất cả các trọng số của một mô hình.
mydict = {'x': x, 'y': y}
npx.save('mydict', mydict)
mydict2 = npx.load('mydict')
mydict2
{'x': array([0., 1., 2., 3.]), 'y': array([0., 0., 0., 0.])}
5.5.2. Tham số mô hình Gluon¶
Khả năng lưu từng vector trọng số đơn lẻ (hoặc các ndarray
tensor
khác) là hữu ích nhưng sẽ mất nhiều thời gian nếu chúng ta muốn lưu (và
sau đó nạp lại) toàn bộ mô hình. Dù sao, chúng ta có thể có hàng trăm
nhóm tham số rải rác xuyên suốt mô hình. Vì lý do đó mà Gluon cung cấp
sẵn tính năng lưu và nạp toàn bộ các mạng. Một chi tiết quan trọng cần
lưu ý là chức năng này chỉ lưu các tham số của mô hình, không phải là
toàn bộ mô hình. Điều đó có nghĩa là nếu ta có một MLP ba tầng, ta cần
chỉ rõ kiến trúc này một cách riêng lẻ. Lý do là vì bản thân các mô
hình có thể chứa mã nguồn bất kỳ, chúng không được thêm vào tập tin một
cách dễ dàng như các tham số (có một cách thực hiện điều này cho các mô
hình đã được biên dịch, chi tiết kĩ thuật đọc thêm trong tài liệu
MXNet). Vì vậy, để khôi phục lại một mô hình
thì chúng ta cần xây dựng kiến trúc của nó từ mã nguồn rồi nạp các tham
số từ ổ cứng vào kiến trúc này. Việc khởi tạo trễ
(Section 5.3) lúc này rất có lợi vì ta chỉ cần định
nghĩa một mô hình mà không cần gán giá trị cụ thể cho tham số. Như
thường lệ, hãy bắt đầu với một MLP quen thuộc.
class MLP(nn.Block):
def __init__(self, **kwargs):
super(MLP, self).__init__(**kwargs)
self.hidden = nn.Dense(256, activation='relu')
self.output = nn.Dense(10)
def forward(self, x):
return self.output(self.hidden(x))
net = MLP()
net.initialize()
x = np.random.uniform(size=(2, 20))
y = net(x)
Tiếp theo, chúng ta lưu các tham số của mô hình vào tệp mlp.params
.
Những khối Gloun hỗ trợ phương thức từ hàm save_parameter
nhằm ghi
tất cả các tham số vào ổ cứng được cung cấp với một chuỗi những tên tệp.
net.save_parameters('mlp.params')
Để khôi phục mô hình, chúng ta tạo một đối tượng khác dựa trên mô hình
MLP gốc. Thay vì khởi tạo ngẫu nhiên những tham số mô hình, ta đọc các
tham số được lưu trực tiếp trong tập tin. Và thật thuận tiện, ta đã có
thể nạp các tham số vào khối thông qua phương thức từ hàm
load_parameters
.
clone = MLP()
clone.load_parameters('mlp.params')
Vì cả hai đối tượng của mô hình có cùng bộ tham số, kết quả tính toán
với cùng đầu vào x
sẽ như nhau. Hãy kiểm chứng điều này.
yclone = clone(x)
yclone == y
array([[ True, True, True, True, True, True, True, True, True,
True],
[ True, True, True, True, True, True, True, True, True,
True]])
5.5.3. Tóm tắt¶
- Hàm
save
vàload
có thể được sử dụng để thực hiện việc xuất nhập tập tin cho các đối tượngndarray
. - Hàm
load_parameters
vàsave_parameters
cho phép ta lưu toàn bộ tập tham số của một mạng trong Gluon. - Việc lưu kiến trúc này phải được hoàn thiện trong chương trình thay vì trong các tham số.
5.5.4. Bài tập¶
- Nếu không cần phải triển khai các mô hình huấn luyện sang một thiết bị khác, theo bạn thì lợi ích thực tế của việc lưu các tham số mô hình là gì?
- Giả sử chúng ta muốn sử dụng lại chỉ một phần của một mạng nào đó để phối hợp với một mạng của một kiến trúc khác. Trong trường hợp ta muốn sử dụng hai lớp đầu tiên của mạng trước đó vào trong một mạng mới, bạn sẽ làm thể nào để thực hiện được việc này?
- Làm thế nào để bạn có thể lưu kiến trúc mạng và các tham số? Có những hạn chế nào khi bạn tận dụng kiến trúc này?
5.5.5. Thảo luận¶
5.5.6. Những người thực hiện¶
Bản dịch trong trang này được thực hiện bởi:
- Đoàn Võ Duy Thanh
- Nguyễn Duy Du
- Phạm Minh Đức
- Lê Khắc Hồng Phúc
- Nguyễn Văn Cường
- Phạm Hồng Vinh