Phát biểu bài toán:
Cho tập ảnh mẫu, từng cặp có một ảnh phác thảo và một ảnh đích. Bài toán đặt ra là, với đầu vào là một ảnh phác thảo, chương trình phải tự sinh ra ảnh đích phù hợp.
Trước hết bài toán image to image là bài toán trong đó đầu vào và đầu ra đều là tấm ảnh. Mục tiêu cuối cùng của bài toán mà chúng tôi nghiên cứu ở đây chính là tạo ra ảnh giày, dép thực từ ảnh phác thảo cho trước.
Chúng ta có tập ảnh mẫu “edges2shoes”. Đây là tập dữ liệu bao gồm
các hình ảnh phác thảo giày, dép và các ảnh màu thật tương ứng của chúng. Sơ lược về bộ dữ liệu bao gồm:
Tập ảnh huấn luyện (Training set): Có 49825 ảnh dùng để học khi huấn luyện.
Tập ảnh kiểm tra (Test set): Có 200 ảnh dùng để kiểm chứng mô hình khi huấn luyện.
Trong đó:
Hình ảnh có tên tệp là chữ số và ở định dạng JPEG. Mỗi hình ảnh có chứa cả hình ảnh giày dép phác thảo ở bên trái và hình ảnh giày dép thực ở
bên phải.
Hình 3.1: Hình ảnh mẫu từ tập dữ liệu
Để giải quyết bài toán này, chúng tôi sử dụng dữ liệu mẫu huấn luyện một mô hình pix2pix để có thể sinh ảnh giả từ ảnh phác thảo phù hợp với ảnh thực trong tập mẫu.
3.2. Xây dựng chƣơng trình thử nghiệm
Tập dữ liệu: Tải xuống bộ dữ liệu tại:
https://people.eecs.berkeley.edu/~tinghuiz/projects/pix2pix/datasets/ed ges2shoes.tar.gz
Để phát triển và đào tạo mô hình Pix2Pix, chúng tôi tiến hành đào tạo đồng thời bộ phân biệt (the discriminator) và bộ tạo (the generator).
Xây dựng bộ phân biệt (the discriminator)
Bộ phân biệt là một mạng nơ-ron phức hợp sâu thực hiện phân loại hình ảnh. Cụ thể là phân loại ảnh có điều kiện. Nó lấy cả ảnh nguồn (ví dụ ảnh phác thảo) và ảnh đích (ví dụ ảnh giày dép thật) làm đầu vào và dự đoán khả năng ảnh đích là thật hay là bản dịch giả của ảnh nguồn.
Để thiết kế bộ phân biệt chúng tôi xác định mối quan hệ giữa một đầu ra của mô hình với số lượng pixel trong hình ảnh đầu vào. Đây được gọi là mô hình PatchGAN và được thiết kế cẩn thận để mỗi dự đoán đầu ra của mô hình ánh xạ tới hình vuông hoặc bản vá 70 × 70 của hình ảnh đầu vào. Lợi ích của cách tiếp cận này là cùng một mô hình có thể được áp dụng cho các hình ảnh đầu vào có kích thước khác nhau.
Để tiến hành đào tạo bộ phân biệt, chúng tôi xây dựng hàm def
Discriminator() để triển khai mô hình phân biệt PatchGAN 70 × 70 theo thiết kế của mô hình. Bộ phân biệt nhận được hai đầu vào: Hình ảnh đầu vào và hình ảnh đích mà nó sẽ được phân loại là thực. Hình ảnh đầu vào và hình ảnh được tạo ra (đầu ra của bộ đào tạo), mà nó sẽ được phân loại là giả mạo.
Bộ phân biệt trong pix2pix là bộ phân loại PatchGAN phức tạp, nó cố
gắng phân loại xem mỗi bản váhình ảnh là thật hay không thật hay. Dựa trên
cấu trúc PatchGAN chúng tôi xác định có những thành phần sau:
(1) Mỗi khối trong bộ phân biệt là: Convolution -> Batch normalization
-> Leaky ReLU.
(2) Hình dạng của đầu ra sau lớp cuối cùng là (batch_size, 30, 30, 1).
(3) Mỗi 30 x 30 bản vá hình ảnh của đầu ra phân loại một 70 x 70 phần
của hình ảnh đầu vào.
Quá trình xây dựng chức năng Discriminator như sau:
Để bắt đầu, chúng tôi xác định bộ khởi tạo trọng lượng lớp tích chập (the convolution layer weight) được lấy mẫu từ một phân phối đồng đều, với giá trị trung bình (mean=0) và độ lệch chuẩn (standard-deviation=0.02.)
Chúng tôi xác định hai lớp đầu vào với kích thước [256,256,3]. Hãy nhớ rằng, bộ phân biệt của chúng tôi là điều kiện trên hình ảnh đầu vào (các cạnh).
Chúng tôi sử dụng hàm tf.concat([inp, tar], axis=-1) để ghép 2 đầu vào này với nhau.
Sau đó, chúng ta có ba khối mẫu xuống (downsample blocks) thông thường, trong đó khối đầu tiên có batchnorm = False. Các lớp Convolution có kernel_size = 4, bắt đầu với 64 bộ lọc (filters). Các bộ lọc này tăng gấp đôi tại mỗi khối mẫu xuống (64-> 128-> 256), dẫn đến kết quả đầu ra là [32, 32, 256].
Chúng tôi tiếp tục xây dựng một lớp zeropadding, lớp đệm mỗi feature map dọc theo cả trục x và y, dẫn đến kết quả đầu ra sẽ là [34,34,256], Điều này được cung cấp thêm cho một khối chuyển đổi (zeropadding layer) có :
Một lớp Conv2D, với kernel_size = 4, 512 bộ lọc (filters) và bước
trượt (stride) là 1, dẫn đến kết quả là [31,31,512]
Sau đó là các lớp Conv2D, Batchnorm và LeakyReLU.
Cuối cùng, chúng ta có thêm một lớp zeropadding nữa và đầu ra của nó được đưa đến lớp Conv2D với kernel_size = 1, stride = 1 và số bộ lọc (filters) là 1 (vì chúng ta chỉ muốn đầu ra 1 kênh).
Tiếp theo, chúng tôi tạo một bản vá (patch) thứ nguyên có kích thước [30, 30, 1]. Ngoài ra, chúng tôi kích hoạt trong lớp này là một hàm sigmoid, tạo ra một xác suất trong phạm vi [0, 1], về khả năng mỗi 1 × 1 từ bản vá 30 × 30 là thật hay giả.
Như vậy, chúng tôi đã xây dựng xong chức năng bộ phân biệt (the discriminator), tiếp theo chúng tôi tiếp tục xây dựng bộ tạo(the generator)
Xây dựng bộ tạo (the generator):
Mô hình bộ tạo phức tạp hơn mô hình phân biệt. Bộ tạo là một mô hình bộ mã hóa-giải mã sử dụng kiến trúc U-Net. Mô hình lấy ảnh nguồn (ví dụ
ảnh giày, dép phác thảo) và tạo ảnh đích (ví dụ ảnh giày, dép thật). Nó thực hiện điều này bằng cách lấy mẫu đầu tiên xuống hoặc mã hóa hình ảnh đầu vào xuống một lớp nút cổ chai, sau đó lấy mẫu hoặc giải mã biểu diễn nút cổ chai thành kích thước của hình ảnh đầu ra. Kiến trúc U-Net có nghĩa là các kết nối bỏ qua được thêm vào giữa các lớp mã hóa và các lớp giải mã tương ứng, tạo thành một hình chữ U.
Để thực hiện mô hình trình tạo bộ mã hóa và giải mã U-Net. Chúng tôi
xây dựng hàm def downsample() để tạo các khối lớp cho bộ mã hóa và
hàm def upsample() để tạo các khối lớp cho bộ giải mã. Chúng tôi xác định: Các chức năng bộ mã hóa (downsample) bao gồm:
Một lớp Conv2D
Một lớp optional BatchNorm
Tiếp theo là chức năng kích hoạt LeakyReLU, với độ dốc ( slope) là 0,2
Các chức năng bộ giải mã (upsample) bao gồm:
Một lớp Conv2DTranspose
Một lớp optional BatchNorm
Một lớp optional Dropout, với drop_probability = 0,5
Tiếp theo là chức năng kích hoạt ReLU, với độ dốc (slope )= 0,2
Trọng số của lớp Convolution trong cả chức năng bộ mã hóa và bộ giải mã được khởi tạo từ phân phối đồng đều, với giá trị trung bình mean=0 và độ lệch chuẩn standard-deviation = 0,02 và không có độ lệch (bias) nào được sử dụng.
Tiếp theo chúng tôi xây dựng UNET Generator, bao gồm một bộ mã hóa (downsample) và giải mã (upsample) dựa trên kết nối bỏ qua. Chúng tôi xác định lớp đầu vào với hình dạng [256,256,3], là hình dạng của hình ảnh mà chúng tôi đã xử lý trước.
Chúng tôi tiến hành xây dựng bộ mã hóa thông qua hàm def
Các lớp bộ mã hóa được xác định là danh sách các lớp trong đó hình ảnh [256, 256, 3] được cung cấp làm đầu vào, được lấy mẫu theo hệ số 2 ở mỗi lần gọi hàm khối mẫu xuống và tổng cộng 8 lần, do đó đạt đến nút cổ chai có kích thước [1, 1, 512].
Tất cả các lớp Conv2D:
Sử dụng kernel_size= 4, với bước trượt (stride) là hai.
Khi đầu vào tại mỗi khối mẫu xuống (downsample block ) được giảm
một nửa (theo chuỗi-chập (strided-convolution)), các feature maps được tăng gấp đôi, bắt đầu từ 64 và tăng lên 512 tại nút cổ chai.
Lớp Batchnorm được sử dụng trong tất cả trừ khối mẫu xuống
(downsample block) đầu tiên.
Chúng tôi tiến hành xây dựng bộ giải mã thông qua hàm def upsample(), như
sau:
Các lớp bộ giải mã được xác định đầu ra có kích thước nút cổ chai [1,1,512] được lấy làm đầu vào, được lấy mẫu (upsampled) thêm theo hệ số 2 tại mỗi khối lấy mẫu ( upsample block). Tổng cộng có 7 lệnh gọi hàm upsample nút cổ chai lên một kích thước [128,128, 128].
Trong bộ giải mã:
Chúng tôi sử dụng lớp Conv2DTranspose, với kernel_size = 4 và
khoảng cách là hai (lấy mẫu thêm hai ở mỗi lớp)
Tiếp theo là lớp BatchNorm và chức năng kích hoạt ReLU, với lớp
dropout trong 1-3 khối mẫu (upsample blocks)
Lớp bộ giải mã cuối cùng cuối cùng cũng nâng cấp đầu ra
[128,128,128] từ khối mẫu (upsample block) lên thành một hình ảnh có kích thước [256,256,3].
Để lấy hình ảnh (RGB) làm đầu ra, chúng tôi sử dụng ba bộ lọc
Tanh là chức năng kích hoạt cho lớp cuối cùng vì dữ liệu của chúng ta hiện đã được chuẩn hóa trong phạm vi [-1, 1].
Bây giờ chúng tôi đã hoàn tất việc xác định cấu trúc bộ mã hóa và bộ giải mã của mình, để tiến hành đào tạo generator chúng tôi cần phải lặp lại lệnh down_stack và up_stack.
Trong khi lặp qua từng phần tử của danh sách down_stack chúng tôi sử dụng lệnh nối đầu ra của từng phần tử trong một danh sách qua hàm
skips(). Đây là một bước quan trọng vì nó sẽ giúp thực hiện các kết nối bỏ qua giữa các lớp bộ mã hóa và bộ giải mã. Chỉ khi chúng ta đảo ngược thứ tự thì các lớp ở đầu bộ mã hóa mới có thể ghép nối với các lớp cuối của bộ giải mã và ngược lại.
Sau đó, chúng tôi lặp lại danh sách up_stack, nén với danh sách bỏ qua (cả hai đều có các phần tử bằng nhau, tức là 7). Khi chúng tôi lặp lại từng phần
tử, kết quả đầu ra được lấy mẫu được nối với một phần tử từ danh sách Skips.
Như vậy, chúng tôi đã xây dựng xong chức năng bộ tạo (the generator), tiếp theo chúng tôi thực hiện tính toán tổn thất bộ tạo và bộ phân biệt.
Xác định tổn thất bộ tạo và bộ phân biệt
Mô hình phân biệt được đào tạo trực tiếp trên hình ảnh thực và được tạo, trong khi mô hình trình tạo thì không.
Thay vào đó, mô hình bộ tạo được đào tạo thông qua mô hình phân biệt. Nó được cập nhật để giảm thiểu tổn thất do bộ phân biệt dự đoán cho các hình ảnh được tạo được đánh dấu là “thực ”. Vì vậy, nó được khuyến khích để tạo ra nhiều hình ảnh thực hơn. Bộ tạo cũng được cập nhật để giảm thiểu tổn thất L1 hoặc sai số tuyệt đối trung bình giữa hình ảnh được tạo và hình ảnh đích.
Bộ tạo được cập nhật thông qua tổng có trọng số của cả tổn thất đối nghịch và tổn thất L1, trong đó chúng tôi đề xuất trọng số từ 100 đến 1 có lợi cho tổn thất L1. Điều này là để khuyến khích trình tạo mạnh mẽ hướng tới
việc tạo ra các bản dịch hợp lý của hình ảnh đầu vào, và không chỉ là các hình ảnh hợp lý trong miền đích.
Các chức năng generator_loss được đưa bốn thông số:
disc_generated_output: Dự đoán đầu ra từ bộ phân biệt, khi được
cung cấp hình ảnh do bộ tạo tạo ra.
gen_output: Hình ảnh được tạo bởi bộ tạo
target: Hình ảnh Ground-truth cho đầu vào được cung cấp cho bộ tạo
real_labels: Nhãn Ground-truth là (1). Bởi vì chúng tôi muốn bộ tạo tạo ra
hình ảnh thực bằng cách đánh lừa người phân biệt, do đó các nhãn sẽ là 1.
Bộ phân biệt được cập nhật theo cách độc lập, vì vậy trọng số được sử dụng lại trong mô hình tổng hợp này nhưng được đánh dấu là không thể đào tạo. Mô hình tổng hợp được cập nhật với hai mục tiêu, một mục tiêu chỉ ra rằng hình ảnh được tạo là thực (mất mát chéo), buộc cập nhật trọng lượng lớn trong trình tạo để tạo ra hình ảnh thực hơn và bản dịch thực được thực thi của hình ảnh, được so sánh với đầu ra của mô hình bộ tạo (tổn thất L1).
Sự mất mát của bộ phân biệt là trung bình của real_loss và generated_loss. Tổn thất Binary Cross-Entropy được sử dụng.
real_loss được tính toán giữa các dự đoán thực (khi hình ảnh thực
được cung cấp cho bộ phân biệt) và real_labels = 1
fake_loss tổn thất được tính toán giữa các dự đoán giả (khi hình ảnh do
bộ tạo tạo ra được cung cấp cho bộ phân biệt) và fake_labels = 0
Sau khi hoàn thành việc xây dựng và tính tổn thất của bộ tạo và bộ phân biệt, chúng tôi có thể cung cấp hình ảnh phác thảo nguồn làm đầu vào cho mô hình và sử dụng nó để dự đoán hình ảnh giày, dép thật. Sau đó, vẽ một số hình ảnh trong quá trình đào tạo.
Tạo hình ảnh:
Để thực hiện tạo hình ảnh chúng tôi tiến hành các bước: Đầu tiên, truyền hình ảnh từ bộ thử nghiệm sang bộ tạo. Sau đó bộ tạo sẽ dịch hình ảnh đầu vào thành đầu ra. Cuối cùng, chúng tôi có thể vẽ biểu đồ nguồn, hình ảnh
được tạo và hình ảnh mục tiêu mong đợi thông qua hàm def
generate_images()
#def generate_images(model, test_input, tar):
Chức năng này có thể được gọi với mỗi hình ảnh nguồn, hình ảnh được tạo và hình ảnh mục tiêu của chúng tôi. Khi chạy kết quả sẽ chọn một hình ảnh ngẫu nhiên từ tập dữ liệu đào tạo, dịch nó sang ảnh giày, dép thật và vẽ biểu đồ kết quả so với hình ảnh mong đợi.
Kết quả có thể thay đổi do bản chất ngẫu nhiên của thuật toán hoặc quy trình đánh giá, hoặc sự khác biệt về độ chính xác số. Chúng tôi xem xét chạy kết quả một vài lần và so sánh kết quả trung bình.
Cuối cùng, chúng tôi có thể đào tạo các mô hình bộ đào tạo và bộ phân biệt.
Đào tạo mô hình:
Để thực hiện đào tạo mô hình, chúng tôi thực hiện các bước: Đầu tiên, chúng tôi xác định đối với mỗi ví dụ đầu vào tạo ra một đầu ra. Bộ phân biệt nhận input_image và hình ảnh được tạo ra làm đầu vào đầu tiên. Đầu vào thứ hai là input_image và target_image. Tiếp theo, tính toán bộ tạo và tổn thất bộ phân biệt. Sau đó, tính toán các mức độ mất mát liên quan đến cả biến trình tạo và biến phân biệt (đầu vào) và áp dụng chúng cho trình tối ưu hóa.
Chúng tôi xây dựng hàm train_step () thực hiện điều này, lấy trình tạo,
bộ phân biệt, mô hình tổng hợp đã xác định và tập dữ liệu đã tải làm đầu vào. Mỗi bước đào tạo trước tiên bao gồm việc chọn một loạt các ví dụ thực,
sau đó sử dụng trình tạo để tạo một loạt các mẫu giả phù hợp bằng cách sử dụng các hình ảnh nguồn thực. Bộ phân biệt sau đó được cập nhật hàng loạt ảnh thật và sau đó là ảnh giả.
Tiếp theo, mô hình bộ tạo được cập nhật cung cấp các hình ảnh nguồn thực làm đầu vào và cung cấp các nhãn lớp 1 (thực) và các hình ảnh đích thực làm đầu ra dự kiến của mô hình cần thiết để tính toán tổn thất. Bộ tạo có hai điểm tổn thất cũng như điểm tổng có trọng số được trả về. Ở đây, tổng tổn thất của mô hình là tổng tổn thất của cả bộ tạo và bộ phân biệt. Chúng tôi sử dụng công cụ TensorBoard.dev trong Tensorflow để cập nhật các tham số của mô hình.
Thông thường, các mô hình GAN không hội tụ; thay vào đó, một điểm cân bằng được tìm thấy giữa các mô hình bộ tạo và bộ phân biệt. Như vậy, chúng ta không thể dễ dàng đánh giá khi nào thì nên dừng việc đào tạo. Do đó, chúng tôi có thể lưu mô hình và sử dụng nó để tạo các bản dịch mẫu từ ảnh sang ảnh theo định kỳ trong quá trình đào tạo, chẳng hạn như cứ sau 20 epochs.
Sau đó, chúng tôi có thể xem lại các hình ảnh đã tạo khi kết thúc quá trình đào tạo và sử dụng chất lượng hình ảnh để chọn một mô hình cuối cùng.
Để làm được điều này trong vòng lặp đào tạo thực tế, chúng tôi xây
dựng hàm def fit() thực hiện các bước: Lặp lại số lượng epochs. Tại mỗi
epochs: xóa màn hình và chạy generate_images để hiển thị tiến trình. Tại mỗi epochs: lặp qua tập dữ liệu huấn luyện, in một dấu chấm ( .) để biểu thị tiến trình cho từng ví dụ.
Cuối cùng, chúng tôi thực hiện chạy vòng lặp đào tạo bằng lệnh:
Chúng tôi nhận thấy rằng:
Hình ảnh được tạo sau khoảng 50 epochs đào tạo bắt đầu trông rất thực tế, ít nhất là có nghĩa, và chất lượng dường như vẫn tốt hơn trong phần còn lại của quá trình đào tạo. Hình ảnh được tạo ra ngoài cùng bên phải; hình ảnh