วาดรูปตัวละครอนิเมะแบบเต็มตัว!? ด้วย Deep Learning— GANime-Fullbody

HIRUNKUL PHIMSIRI
10 min readJun 21, 2022

--

สวัสดีครับ ผมไกด์ หิรัญกุล พิมพ์ศิริ
เด็กศิลป์ภาษาอินดี้ที่เขียนโปรแกรมแล้วก็มานั่งทำ AI
ตอนนี้ก็ยังสงสัย ว่าทำไมไม่เข้าศิลป์คำนวณ **ฮา**
ตัวผมเองชอบงานศิลปะมากๆ โดยเฉพาะงานตัวละครลายเส้น Anime ตัวละครหญิง
และในขณะเดียวกัน ผมก็เป็นเด็กติดเกม ที่ก็เคยสร้างเกมมาบ้าง
ปัญหาขัดใจที่พบบ่อยๆในการฝึกทำเกมมักจะเป็นเรื่องของ Sprite หรือภาพตัวละคร
ในการฝึกสร้างเกม การหาตัวละครที่ไม่ซ้ำซาก จำเจมาลองใส่ มาลองเล่น
หรืออยากหา Inspire ดีไซน์ตัวละครที่น่าจะเหมาะกับงานของเรา ไม่ได้มีง่ายๆ
เช่นเกมแนว Visual Novel แบบเกมจีบสาว
Design ตัวละครที่สวยงาม เป็นหนึ่งในจุดหลักๆที่ทำให้เกมน่าสนใจ
แต่การลอง Design มันช่างน่าปวดหัว และใช้ระยะเวลา
ผมจึงอยากสร้างโมเดลที่สามารถลองสร้างตัวละครให้ดูเป็นไอเดียได้
บวกกับอยาก Challenge ตัวเองด้วยอะไรยากๆบ้าง ไม่อยากทำโมเดลประเภทเดิมๆ

ตัวอย่างผลลัพธ์

ในปัจจุบัน มีโมเดล Deep Learning ที่สามารถวาดรูปลายเส้น Anime
ออกมาได้สวยงามอยู่มาก ไม่ว่าจะเป็นด้วย VAE หรือด้วย GANs
แต่ยังไม่มีโมเดล Public ใดๆ ที่ทำ Generative Model
สำหรับวาดภาพตัวละคร Anime แบบเต็มตัว อย่างมากก็แค่ครึ่งท่อนบนเท่านั้น

ตัวอย่าง GANs Generated Anime Faces: จาก— https://www.thiswaifudoesnotexist.net/

ผมจึงอยากสานต่อการทำ Waifu Generator ด้วยการสร้างโมเดล
ที่สามารถวาดแบบเต็มตัวได้ เพื่อสนองความต้องการเสพงานวาดของตัวเอง
พร้อมช่วยเรื่องการทำเกม และการ Design ตัวละครไปด้วยในตัว
โดยโมเดลที่ผมเลือกใช้คือ GANs หรือ Generative adversarial networks
เพราะสามารถเก็บรายละเอียดได้ชัดกว่า VAE
โดยเป้าหมายของผมคือภาพที่ดูดี ในความชัดที่ 32², 64² หรือ 128² Pixels

สารบัญ
— Gans คืออะไร
— Datasets ชุดข้อมูลและการวิเคราะห์
— Data Cleaning ทำความสะอาดข้อมูล
— Model โมเดล
— Conclusion สรุปผล
— Deployment
— ทดลองใช้โมเดล
— คำขอบคุณ

GANs คืออะไร

Gans หรือ Generative Adversarial Networks, คือประเภทของ frameworks
ในการทำ Machine Learning เพื่อสร้างข้อมูลขึ้น โดย Ian Goodfellow ในปี 2014
หลังจากการเผยแพร่, ก็มีการวิจัยพัฒนา และต่อยอดจาก GANs ดั้งเดิมมากมาย
แต่หลักๆแล้ว โมเดล GANs จะประกอบไปด้วยส่วนประกอบหลักๆ 2 ส่วน
คือ Generator และ Discriminator ซึ่งผมจะอธิบายเป็นคอนเซ็ปต์
ด้วยตัวอย่างจากเคส ตำรวจตรวจแบงค์ปลอม กับนักทำธนบัตรปลอม

Generator — นักทำธนบัตรปลอม

ก่อนอื่น เรามีนักทำธนบัตรปลอม ซึ่งก็น่าจะเดากันได้ว่าหน้าที่ของเขา
คือการทำธนบัตรปลอมให้เหมือนที่สุด เพื่อหลอกตาตำรวจให้ได้

Discriminator — ตำรวจ

ในทางตรงกันข้าม ตำรวจ หรือ Discriminator มีหน้าที่ในการตรวจสอบธนบัตร
และแยกให้ออกว่า ธนบัตร เป็นของปลอม หรือของจริง
ก็จะเห็นได้ว่า Discriminator ก็คือ Classification Model ตัวนึงครับ

เก่งเจ๋งไปด้วยกัน

เราจะให้ทั้งคู่ สู้กันในเกมธนบัตรปลอมไปเรื่อยๆ (ภาพ 1.1) คนหนึ่งพยายามจะหลอก
อีกคนหนึ่งพยายามจะจับ การสลับกันแพ้สลับกันชนะนี้ จะทำให้ทั้งคู่เก่งขึ้นเรื่อยๆ
ตำรวจก็จะพยายามหาช่องโหว่ในธนบัตรปลอม ที่แตกต่างจากธนบัตรจริง
ในขณะที่นักทำธนบัตรปลอม พยายามปิดช่องโหว่ที่เคยโดนจับได้
ส่งผลให้กลายเป็นการฝึกฝนทั้งคู่ไปในตัว จนถึงจุดหนึ่ง
ที่นักทำธนบัตรปลอม จะทำธนบัตรได้เหมือนมาก จนแยกจากของจริงไม่ได้

(1.1) ภาพอธิบายโมเดล Gans: cell.com/trends/cognitive-sciences/fulltext/S1364-6613%2821%2900153-4

Note: แม้ว่าจะมีโมเดล GANs หลากหลายโมเดล แต่ทั้งหมดก็ประกอบไปด้วย
Generator และ Discriminator เป็นตัวสำคัญ
ในโปรเจ็คนี้เราจะใช้ : dcgans, wgan-gp, progans, stylegans ซึ่งท้าวความไว้ก่อน

รายละเอียดโมเดลเชิงลึกอยู่ใน Trainning — สร้างโมเดล

Datasets — ชุดข้อมูลและการวิเคราะห์ข้อมูล

ก่อนที่จะลงไม้ลงมือกับการเขียนโปรแกรม และการทำโมเดลของเรา
สิ่งสำคัญที่สุดก็คือชุดข้อมูลที่ดี GANs เองก็ไม่ใช่ข้อยกเว้น
เช่นเนื่องด้วยเราต้องการภาพตัวละครแบบเต็มตัวในท่ายืน และให้นำไปใช้งานได้
เราไม่ต้องการพื้นหลัง เพราะจะเพิ่มความยากให้โมเดล และการนำไปใช้งาน

ผมจึงได้ตั้งบรรทัดฐาน ของชุดข้อมูลไว้ดังนี้
- ไม่มีพื้นหลัง (ขาวล้วน)
- ลายเส้นและลักษณะตรงความต้องการ (เต็มตัว, ลายเส้นญี่ปุ่น, ตัวละครหญิง)
- ชุดข้อมูลที่มีขนาดใหญ่ เนื่องจาก Gans ต้องใช้ชุดข้อมูลจำนวนมาก (10k ++)

แต่จาก Datasets ที่มีอยู่มากมายบนโลก
ไม่มีอันไหนเลยที่ตรงตามความต้องการของผม หรือนำมาใช้งานแทนได้
ซึ่งการไม่มีชุดข้อมูลเปิดที่ใช้งานได้เลยนั้น อาจจะเป็นหนึ่งในเหตุผลหลักๆ
ที่ไม่มีการพัฒนา GANs อนิเมะแบบเต็มตัวออกมาให้ใช้งานได้ง่ายๆ

Data Collection — ข้อมูลไม่มีก็ทำเองได้

เนื่องจากไม่ Datasets ให้ใช้ ผมจึงตัดสินใจที่จะรวบรวม Datasets ขึ้นมาเอง
โดยแหล่งข้อมูลที่ผมเลือกที่จะ Scrape จะต้องคุ้มค่าที่จะรวบรวม

ผมจึงได้ตั้งบรรทัดฐาน ในการรวบรวมชุดข้อมูลไว้ดังนี้
- ง่ายในการคัดกรอง (แยกประเภทชุกข้อมูลได้)
- มีข้อมูลจำนวนมาก (10k ++)
- ตรงกับบรรทัดฐานของชุดข้อมูล

จากการรวบรวมและคัดเลือกแหล่งข้อมูลเป็นเวลาหลายวัน ผมก็ได้รายชื่อมาดังนี้

รายชื่อแหล่งข้อมูล(เว็บไซต์)

ผมเลือก Gelbooru และ Getchu ออกมาเพื่อเปรียบเทียบเพิ่มเติม (เพราะดีที่สุดทั้งคู่)

Gelbooru — ข้อมูลดี แต่มี 18+ เพียบ

Gelbooru เป็นเว็บไซต์ภาพวาด Anime ที่มีฐานข้อมูลใหญ่มากเป็นอันดับต้นๆ

ข้อดี:
- มีระบบ Filter ที่ละเอียดมาก ทำให้การ ทำความสะอาดข้อมูลง่ายขึ้น
- ง่ายในการรวบรวม

ข้อเสีย:
- เนื่องจากไม่ได้มีการจำกัดเรททำให้มีภาพโป๊เปลือยอยู่มาก
- ไม่ได้การันตีว่าเราจะได้ภาพที่มีลักษณะการใช้งาน และสไตล์ไกล้เคียงกัน
- แม้จะมี Filter ที่แม่นยำ แต่ก็ Label โดยผู้อัพโหลดทำให้มีหลุดภาพแปลกๆมาบ้าง

Getchu — ของดีกว่า แต่ดันเอามาไม่ได้

เป็นร้านค้าเกม Visual Novel ออนไลน์ ที่มีการโชว์ภาพตัวละคร

ข้อดี:
- เป็นภาพสำหรับใช้ในเกมแบบที่อยากได้
- ลายเส้นไม่หนีห่างกันมาก
- ไม่มีภาพโป๊เปลือย

ข้อเสีย:
- มีภาพแบบครึ่งตัวปนอยู่ด้วย
- เว็บไซต์ค่อนข้างไม่เสถียร เพราะอยู่เซิฟไกลกัน

ผมจึงตัดสินใจเลือก Getchu เพราะได้ข้อมูลที่มีคุณภาพกว่ามาก
แต่ในช่วงเวลาที่กำลังเริ่มรวบรวมข้อมูล ผมไม่สามารถเชื่อมต่อไปที่ Getchu ได้
ทำให้สุดท้ายแล้วผมจึงจำเป็นต้องเลือก Gelbooru
แต่ปัญหาคือ Gelbooru มีรูปเปลือยเยอะมาก ผมจึงต้องหาทางออกให้ได้

Safebooru — Gelbooru แต่ไม่ 18+

ผมได้เจอกับ Safebooru เว็บไซต์ที่มีทุกอย่างเหมือนกัน แต่ไม่มีภาพเปลือย
และในขณะเดียวกัน ก็ได้เจอ Library สำหรับการ Scrape เว็บไซต์นี้ด้วย
GitHub — LittleJake/animate-image-crawler: A crawler for booru site.
ซึ่งทำให้การทำงานของผมเร็วขึ้นมาก

Website safebooru.org

เนื่องจาก Safebooru ใช้ระบบ Tags ในการกรองข้อมูล
ในการรวบรวมข้อมูล เราจึงพยายามจะใช้ Tags ที่ดีที่สุดเพื่อลดปัญหาในอนาคต

จากการลองหลายๆรอบ ผมพบว่า Tags

full_body solo standing 1girl white_background

ให้ผลลัพท์ที่ดี โดยยังมี Datasets มากและหลากหลาย

full_body solo standing 1girl white_background

หลังจากการรวบรวมข้อมูล เราได้ข้อมูลมาทั้งหมด 2 หมื่นรูป สิ่งที่ต้องทำต่อจากนี้คือ

Data Cleaning — ศึกษาและทำความสะอาดข้อมูล

หลังจากได้ Raw Datasets มาแล้ว สิ่งที่เราจะต้องทำต่อไปคือการวิเคราะห์ข้อมูล
เพื่อคัดแยกเอาข้อมูลที่ไม่ได้ตรงกับความต้องการ และอาจทำให้โมเดลมีปัญหาออก
โดยอ้างอิงจากบรรทัดฐานที่กล่าวไปก่อนหน้านี้

วิเคราะห์ข้อมูล

จากชุดข้อมูล 2 หมื่นรูป จากการทำความสะอาดข้อมูลด้วยมือมา 2 พันรูป
โดยอิงดุลพินิจตามบรรทัดฐานข้างต้น จะพบว่าเราสามารถแบ่งข้อมูลได้เป็น

ตัวละครจิบิ (หัวโต ตัวเล็ก) มีจำนวน ~ 1:4 ของข้อมูลที่คัดออก

ตัวอย่างตัวละคร จิบิ

มีมากกว่า 1 ในภาพเดียว อัตราส่วน รองลงมาจาก จิบิ ~ (1:8)

ตัวละครมีมากกว่า 1

พื้นหลังฉูดฉาด เอฟเฟคเยอะ & ท่ายืนแปลกๆ

โยคะโพส
เอฟเฟคเยอะ

แต่สองส่วนหลังมีอัตราส่วนน้อยมากๆ จึงน่าจะสามารถทำความสะอาดเองได้
ประเด็นหลักๆอยู่ที่การเอา จิบิ และ มากกว่า 1 ออก

Data Cleaner — ใช้ Classifier ช่วยคลีน

เราแบ่ง Datasets มาใช้เทรน Chibi Classifier และ more Than 1 Classifier
โดยแบ่งเป็น 2 classes คือ [‘จะเก็บไว้’, ‘จะแยก’]

Crop รูปพร้อม Padding เผื่อไม่ให้เสีย Aspect Ratio

Chibi Datasets
More Than One Datasets

เทรน 2 โมเดลสำหรับ Chibi(~1k datasets) และ More Than 1(~0.3k datasets)
แล้วเช็ค Accuracy บน Testset ที่ split มา

ความแม่นยำ Chibi Classifier

Chibi Classifier

ความแม่นยำ More Than 1 Classifier

More Than 1 Classifier

ความแม่นยำของทั้งคู่ออกมาดีมากๆ ผมจึงตัดสินใจจะเอามาใช้ในการคลีนข้อมูล

ตัวอย่าง Chibi ที่ถูกคลีนออก
ตัวอย่าง More Than 1 ที่ถูกคลีนออก

จาก ชุดข้อมูล 2 หมื่นรูป เราทำความสะอาดข้อมูลด้วยโมเดลไปทั้งหมด 6 พันรูป
- จิบิ 5k~ รูป
- มากกว่าหนึ่ง 1k~ รูป
- ด้วยมือ 2k~ รูป
เหลือ Datasets ทั้งหมด 12k~ รูป

เอาไฟล์ขึ้น Drive แล้วจากนั้นเราก็ไปหาวิธีทำโมเดลกันเลยดีกว่า

Model & Analyze—โมเดลและการทดสอบข้อมูล

เราเริ่มต้นด้วยการลองโมเดลที่ง่ายที่สุด อย่าง DCGans ที่เป็น Gans ธรรมดาๆ
ที่ใช้ CNN ในการดึง Feature ของรูป แต่ผลที่ออกมาก็เละเทะมากๆ
ออกมา เป็นแค่ Noise และยังไม่ดีขึ้นหลังจากเทรนต่อไปกว่า 500 Epochs
ซึ่งเกิดขึ้นจากการที่ความง่ายที่โมเดล Discriminator
จะเก่งกว่า Generator นั้นมีมาก ทำให้ Generatorไม่ได้เรียนรู้อะไรเลย
(งานของ Discriminator ง่ายกว่า เพราะแค่แยกรูป Generator ต้องวาดทั้งอัน)

Sample Of RGB Noise (Not Real)

ซึ่งอาการนี้ก็คือ Vanishing Gradient ซึ่งเป็นการที่ Prediction ของ Discriminator
แยกแยะของปลอมได้ดีเกินไปจน Loss เข้าไกล้ 0 เร็วมากๆ
ทำให้ Gradient ออกมาเป็นค่าที่เข้าไกล้ 0 ด้วยเช่นกัน
ส่งผลให้โมเดล Generator หยุดพัฒนา (เรียนรู้ช้ามากๆ)

ปัญหานี้เกิดจาก BCE loss — Binary Cross Entropy Loss

BCE loss — Binary Cross Entropy Loss

ซึ่งเมื่อโมเดล Discriminator ทำได้ดีขึ้นมากๆค่า Loss จะเข้าไกล้ 0 ด้วยความรวดเร็ว

BCE loss — เมื่อ Pred(x) = 1/0

เมื่อทำการคิด Gradient แล้ว ค่าที่ได้ก็เลยจะเข้าไกล้ 0 เช่นกัน
หลังจากคูณ Learning Rate < 1 ค่าที่ได้ก็จะยิ่งน้อยลงไปอีก
ส่งผลให้ Weight ของ Generators แทบไม่โดนเปลี่ยนเลย
และทำให้ Generetor ไม่ได้เรียนรู้อะไรเลย

เราทำการเปลี่ยน Loss ของโมเดล เป็น Wasserstein loss — WLoss

Wasserstein loss

ที่จะให้ Output โดยไม่จำกัดขอบเขตเป็น 0<->1
Note: เนื่องจาก output ไม่ได้เป็นของเขตของ 2 classes
จึงไม่ได้ถูกเรียกว่า Discriminator แล้ว แต่ถูกเรียกว่า Critic แทน
แต่ในบทความนี้จะยังใช้คำว่า Discriminator ต่อไป

แต่ WLoss ก็ยังจะมีประเด็นในการที่เมื่อค่า Loss ไม่มีการจำกัดขอบเขต
ก็จะทำให้ค่า Loss วิ่งสูงเสียดฟ้าได้ไม่มีจำกัด
เมื่อคิด Gradient ทำให้ออกมาได้ค่าที่เยอะมากๆ
ซึ่งนั่นจะทำให้เกิด Exploding Gradient

ซึ่งก็คือขั้วตรงข้ามของ Vanishing Gradient เป็นการที่ค่า Gradient พุ่งสูงเกินไป
จนโมเดลไม่ได้เรียนรู้อะไรอีกเช่นกัน
เราจะแก้ปัญหานี้ด้วยการพยายามทำให้ค่า Gradient ลดลงเมื่อมากกว่า 1

ด้วย Gradient Penalty

ปล.ค่า Lambda เป็น Magic Number

ค่า GP ที่คูณกับ Lambda นั้นมาจากการ Interpolate
ข้อมูลภาพ Real กับ Fake แล้วก็ได้ภาพ Random Interpolate มาเพื่อคิด Prediction

ค่า Epsilon (เป็น Magic Number)

Random Interpolation Image นั้นจะเป็นตัวแทนของ Real & Generated

คูณกับ Lambda ที่เป็น Magic Number ค่า GP จะช่วยให้ Loss เพิ่มด้วย
ถ้าเกิดค่า Gradient เพิ่มขึ้น ทำให้โมเดลจะพยามลด Loss โดยไม่ใช้ Gradient ที่สูง

หลังจากเทรนไป ~100 epochs

128x128 : latent Size 512 (WGAN-GP Generated)

จากการที่โมเดลจับดีเทลเป็นรูปร่างได้ เป็นการทดสอบ
ว่า Datasets ของผมสามารถใช้งานได้
ประเด็นที่น่าสนใจคือดีเทลต่างๆไม่ชัดเจน เช่น เสื้อผ้าหน้าผม
ผมตัดสินใจหาวิธีใหม่ในการเทรนโมเดล ซึ่งผมตัดสินใจใช้ Progressive Gans

Progressive Growing Of Gans — สเกล Gans Progressively!

Progressive Gans หรือ Progans คือการสเกล Convolutional Layer
ตามระยะเวลาการเทรน เพื่อให้ Model ค่อยๆสร้างภาพที่ละเอียดขึ้นเรื่อยๆ

ภาพจาก paper ของ progans

Progressive Gans หรือ Progans ให้ผลลัพท์ที่ดีกว่า ในการสร้างภาพรายละเอียดสูง
เนื่องจากการค่อยๆ Scale Layer ของ CNN ทั้งใน Discriminator และ Generators ทำให้โมเดลจะค่อยๆจับ Feature ที่ความละเอียดต่ำๆก่อน ในภาพ 4²
เมื่อคุ้นชินแล้วก็จะเริ่มปรัปความละเอียดในสูงขึ้น เป็น 8² แล้วค่อยๆเพิ่มไปเรื่อยๆ
จนถึง Scaling เป้าหมาย ที่ 128² (ในตัวอย่างจาก Paper คือ 1024²)
ซึ่งก็เหมือนกับการสอนให้เด็กเรียนรู้อะไรยากๆได้ ต้องเริ่มมาจากเรื่องง่ายๆ
ที่เป็นพื้นฐานของเรื่องนั้นก่อน การทำแบบนี้จึงดูมีเหตุผลกว่าการให้ภาพยากๆไป
แล้วหวังว่า Discriminator จะจับ Feature เล็กๆน้อยๆของภาพได้ดี
เพราะโดยปกติแล้ว Classification Model นั้นไม่ได้จำเป็นต้องจับทุก Feature ที่มี เพื่อให้ได้ผลลัพท์ที่ดี แต่สำหรับ Gans แต่ละส่วนล้วนมีความสำคัญ
เพราะ Feature เล็กน้อยเหล่านั้นจะถูกส่งต่อไปที่ Generators ทำให้ภาพละเอียดขึ้น

การ Scale Layers ของ ProGans ทำด้วยการค่อยๆ Upsample Layer ก่อนหน้าขึ้นไป
ด้วยอัตรา (1- Alpha) ค่าคงตัว Alpha จะค่อยๆเพิ่มขึ้นในทุกๆ iterations จนเป็น 1

(b) คือการ Scale Layer ของ Progans

หลังจากเทรนไป 4 Steps (200, 150, 100, 50) เป้าหมายที่ 32²
(ยังคงใช้ Wloss และ Gradient Penalty | แค่เพิ่มความ Progressive เข้าไป)

ภาพวาดโดย progans ที่ 32²

จะเห็นว่าภาพ มี Distribution ที่หลากหลายขึ้น ไม่ว่าจะเป็น เสื้อผ้าหรือสีสัน
ต่างจาก DC Gans ที่มีการที่ใช้สีโทนเดียวและเปลี่ยนโทนไปเรื่อยๆ (mode collapse)
(ไม่มีการเทียบวัดค่า FID / Matrices อื่นๆของทั้งสองโมเดล เพราะผลต่างกันชัดเจน)
แม้จะดีขึ้น แต่ยังคงหลงเหลือ pixel ที่สีออกมาแปลกๆ โมเดลขาลีบ ขาขาด ผมเละ
สรุปแล้วคือโมเดลยังคงมีปัญหาในการสร้างรายละเอียดที่ละเอียดมากๆเหมือนเดิม

นอกจากนี้ปัญหาของ ProGans คือการ scale layer แต่ะละรอบ ใช้เวลานานขึ้นมาก
เราจึงไม่สามารถลอง Result ที่ 128² ที่เป็นเป้าหมายได้
เนื่องจากระยะเวลา และ Resource จึงทำจากการวิเคราะห์ผลลัพธ์ที่ Scale 32²

Style-Gans — Generator Upgrade!!!!

สิ่งที่เราทำทั้งหมดในโมเดลก่อนๆ คือการเพิ่มความสามารถของ Discriminator ให้ส่งข้อมูลที่สำคัญไปที่ Generator ได้มากขึ้น แต่ในคราวนี้
เราจะเพิ่มความสามารถให้ Generator ด้วยการใช้วิธีการของ Style Gans

Style Gans Generator ประกอบไปด้วย Components หลักๆ 3 อย่าง คือ
1. Progressive Growing Of Gans — ใช่แล้ว ProGans ที่เราพึ่งใช้ไปนั่นเอง
2. Mapping Network — ใช้สำหรับจัดเรียง Random Noise ให้มีรูปแบบ
3. AdaIN — Style Transfer

ผมจะข้ามในส่วนของ Progans ไปเพราะเคยอธิบายไปแล้ว
แต่ส่วนสำคัญคือเราะจะเปลี่ยน Architecture ของโมเดล Generator

ตัวอย่างเฉพาะโมเดล Generator

จากทางด้านซ้าย เป็นด้านขวา ซึ่งแตกต่างกันอย่างเห็นได้ชัด (Synthesis Network)
เพิ่ม AdaIN หรือ Adaptive Instance Normalization เข้าไป และเปลี่ยนการใส่ Noiseโดยมีค่าคงที่ตั้งต้น ขนาด 4x4x512 ที่จะวิ่งผ่าน Scale 4x4 + Noise ทุก Layers
ไม่ได้ใส่ Noise เข้าไปตรงๆ แต่เป็นการใส่เข้าไปที่แต่ละ Layers (Noise ที่วิ่งผ่าน[B])
เพิ่อสร้างความ Random หรือ Style ที่หลากหลายให้กับ ภาพ G(x)
แต่ยังใช้หลักการในการ Upsamples จาก Resolution น้อยๆ เหมือน ProGans เดิม

Mapping Network —Map Noise เป็น Style
Mapping Network ประกอบด้วย Fully Connected Layer (dense/linear)
ใช้เพื่อการ Map Latent Z เพื่อพยายามให้ Style นั้นแบ่งแยกกัน
พูดให้เข้าใจง่ายคือ มีข้อมูลจากการศึกษา ProGans
ที่พบว่าแต่ละ Layer ให้ความสำคัญกับ Feature ที่แตกต่างกัน
เช่นเลเยอร์แรกๆ ให้ความสำคัญกับ รูปร่างที่เป็นมนุษย์
เลเยอร์กลางๆเป็น รูปทรงของหน้า และ Layer ท้ายๆ เป็นรายละเอียดเชิงลึก
เช่นสีตา ลูกตา เส้นผม ทรงผม แว่น กระบนใบหน้า
การปรัปเปลี่ยนข้อมูลเฉพาะเหล่านี้ จะสามารถส่งผลให้เรา ควบคุม Output ได้

Zᵢ มีผลกับ Output หลายส่วน

แต่ปัญหาคือ Latent Vector Z ที่ใช้ในการเรียนรู้ข้อโมเดลโดยปกติไม่ได้มี Format
ที่มีแค่ Zᵢ บางตัว Represent ข้อมูลเพียงอย่างเดียว การปรัป Zᵢ จึงจะทำให้ส่วนอื่นๆ
เปลี่ยนตามไปด้วย เช่นจากตัวอย่างด้านบน การปรัป Zᵢ ของตา (ช่องสุดท้าย)
จะส่งผลให้ตัวเปลี่ยนไปด้วย ทำให้การเปลี่ยน Style เป็นไปได้ยาก
การ Map Layer เป็น W จะช่วยให้ยุ่งเหยิงน้อยลง (อาจจะไม่ถึงขั้น 1z ต่อ 1 Feature)
ทำให้ง่ายขึ้นในการ Generate ภาพออกมาให้ดี

AdaIN — Adaptive Instance Normalization
เราเอา W มาใส่ในทุกๆ Layer ผ่าน AdaIN
เพื่อทำ Style Transfer จาก W ไปที่ Feature Map

W -> [A (transformation)] -> AdaIN

ซึ่งการทำ Style Transfer ครั้งต่อไป ก็จะทำจาก Feature Map
ที่โดนทำ Style Transfer มา แล้วก็ทำไปเรื่อยๆ
[B] คือ Gaussian Noise ที่เราสามารถ Inject(ยัด) เข้าไป
เพื่อให้มีความหลากหลายมากขึ้น แต่ว่าสไตล์ยังคงเหมือนเดิม
เช่นเปลี่ยนแค่ลักษณะรอยยิ้ม

เราทำการเทรน Style Gans Model (A) ด้วย Datasets เดียวกันกับ ProGans
เนื่องด้วยประเด็นเรื่องเวลาและทรัพยากรเช่นเคย เราจะใช้ Result ที่ 64²
ในการตัดสินใจและพัฒนาต่อ สุดท้ายเราจะ Scale วิธีที่คิดว่าดีที่สุด ไปเป็น 128²

หลังจากเทรนไป 5 Step [100, 50, 50, 50, 50]

64² Latent size: 256

ผลออกมาไม่แย่ แต่ก็ไม่ได้ดีมาก เลยลองเปลี่ยน Latent Size กลับเป็น 512

64² Latent size: 512 Style Gans
32² Latent size: 512 ProGans

ถึงแม้จะไม่ได้ดีกว่ากันชัดเจน เพราะไม่ได้เทียบที่ Scale (32²) — ลืมถ่ายมา
แต่ว่าผลลัพท์ของ Style Gans ที่น่าพึงพอใจ ใช้เวลา (Epochs) น้อยกว่ามาก
ผมจึงเลือกที่จะเอาโมเดลนี้มาเป็น Baseline และพยายามทำ Result ให้ดีขึ้น

หลังจากลอง Generate รูปมาดูหลายๆ Generation
https://drive.google.com/drive/folders/10Z0QCnmiWgAV8hiJoNOqBR7infXbHlQJ?usp=sharing

พบว่าไม่มีภาพใส่ Effect หรือทำชุดที่ยากๆออกมาได้สวยเลย

ณ จุดนี้ผมจึงต้องหันกลับไปมองที่ Datasets ของตัวเองอีกครั้ง
ก็พบว่ามีภาพจำนวนมาก ที่มี Effect และชุดมีรายละเอียดสูง ซ้ำยังเฉพาะมาก
จากการที่ไม่มีชุดประเภทนี้ออกมาเป็นผลลัพท์ที่มองเห็นได้เป็นตัวในโมเดล
ผมจึงคาดการณ์ว่า ภาพที่ออกมาไม่ได้ น่าจะเกิดจากการพยายาม Generate
ภาพที่รายละเอียดชุด หรือรายละเอียดตัวละครสูงๆ

ผมเลยกลับมาทำความสะอาดข้อมูลอีกครั้ง เพื่อสร้าง Datasets ใหม่
สำหรับการทดสอบสมมุติฐานนี้

การคลีนข้อมูล
ทำด้วยการคัดแยกข้อมูลที่ไม่เอา และแบ่งเป็นข้อมูลที่ไม่ดี (มีเอฟเฟค, ชุดยาก)
ทั้งหมด 500 รูปถูกทำความสะอาดออกไป จาก 16k

BAD Data ภาพที่ไม่เอา
Good Data ภาพที่จะเก็บไว้

ในครั้งนี้ ผมเลือกที่จะใช้โมเดล Resnet50 ในการคัดแยก
เพราะคิดว่า Pattern ของภาพค่อนข้างที่จะจับยาก ว่าอันไหนดีอันไหนไม่ดี
การไปลอง Resnet34 หรือโมเดลอื่นๆก่อนก็อาจจะเสียเวลา ผมเลยใช้ resnet50 เลย
เพราะ สามารถจับรายละเอียด และดีเทลของรูปได้ดีกว่า

ผลลัพท์สุดท้าย Accuracy 0.86 โดยที่ Precision ที่ class 0[good] ไม่ค่อยแม่นยำ
หมายความว่าถ้าเราเอาไปใช้ในการทำความสะอาดข้อมูล
เราจะเสียข้อมูลได้ถึง 40% แต่อย่างน้อยก็ดีกว่าทำความสะอาดด้วยมือตัวเอง
เพราะช้า และผิดพลาดบ่อย แต่เพื่อความมั่นใจ ผมจึง Plot Prediction มาดู

Plot Prediction

จริงๆแล้วในหมู่ข้อมูลที่ทายผิด กลับผิดเพราะการแยกข้อมูลของผมเองอยู่หลายภาพ
แต่โมเดล Predict ถูก เรียกได้ว่า ในหลายๆรูป โมเดลแม่นกว่าผมที่เป็นคนแยกเองอีก
จริงๆบนซ้ายกับล่างขวา ควรจะเป็น Bad เพราะชุดยาก อุปกรณ์ และรายละเอียดเยอะ
แต่ผมดันเอาไปใส่ Good

จากนั้นผมเลยเลือกที่จะเอาโมเดลนี้ไปทำความสะอาดข้อมูลที่ผมมี

BAD Data — Variety Of Style
Good Data — Simpler Cloths

ซึ่งโมเดลทำความสะอาดข้อมูลไปได้ถึง 11k~ ภาพ!
ทำให้ตอนนี้ Datasets ของเราเหลืออยู่เพียงแค่ 5k ซึ่งน้อยมากๆ
แต่จะเห็นได้ว่าเมื่อมองกว้างๆ แล้ว ชุดของ Datasets ใหม่นั้นง่ายกว่า
ผมได้ลองเอา Datasets ใหม่นี้ ไปเทรน Model(B) ด้วย Config เท่าเดิม

64² Latent Size:

ผลออกมาค่อนข้างที่จะสวยงามเลยทีเดียว แม้จะด้วย Data ที่น้อยลง
https://drive.google.com/drive/u/1/folders/10Z0QCnmiWgAV8hiJoNOqBR7infXbHlQJ

แต่เราก็ไม่สามารถวัดผลทั้งสองอย่างนี้ด้วยตาได้อย่างชัดเจน
ในจุดนี้เราจึงต้องใช้วิธีอื่นในการวัดผลคุณภาพของโมเดล ทั้ง 2

FID — Fréchet inception distance
ใช้การเทียบ Distribution ของข้อมูลสองชุด ซึ่งนิยมใช้วัดผล Gans
โดยใช้การดึงเอา Feature ที่โมเดล CNN สามารถ ดึงมาได้
กรณีนี้คือ Inception V3 แล้วเทียบ Distribution ของ Feature
GitHub — mseitzer/pytorch-fid: Compute FID scores with PyTorch.

ผม Generate ภาพ จากโมเดล Style_Gans(A) และ (B) มา 10k รูป
และเทียบ FID กับ Datasets ของ (A — เพราะเป็นข้อมูลดิบจริงๆ)

Model(A)
Model(B)

จะเห็นได้ว่า Model B มี Result ที่ดีกว่าถึงเกือบเท่าตัว เราจึงจะเอาโมเดล (B)
มาทำการ Upscale ไปเป็น 118² Resolution

ด้วยพลังของ AWS EC2 Instance P3.xlarge Nvidia V100 16GB!

EC2 instance ช่วยให้งานของเราง่ายขึ้นในการเทรนโมเดลเพราะ GPU ที่แรงขึ้น

ตัวอย่างสุ่มๆ จากการ Scale ไปที่ 128² [500] epochs

Conclusion — สรุปผล

แม้จะมีผลลัพท์ที่ออกมาน่ากลัวอยู่บ้าง
แต่โดยรวมแล้วผลลัพท์ที่ออกมา ถือว่าน่าพึงพอใจ
โมเดลสามารถจับดีเทลขาได้อย่างสวยงาม ชุดที่ถูกลักษณะ ร่างกายที่สมส่วน
และยังพยายามเติมหน้าเข้าไปในทุกๆโมเดล แต่ผมยังทำได้ชัดมากกว่านี้
และผมยังใช้ความสามารถของ Style Gans ไม่เต็มที่เท่าที่ควร
สิ่งที่ผมจะพัฒนาต่อหลังจากนี้คือ
— การทดลองปรัป Noise ของโมเดล และทดสอบ Latent W เพื่อหา Style แต่ละส่วน
— การทำให้ภาพชัดขึ้นที่ 256²
— การหา Datasets ที่ดีขึ้นเพื่อเพิ่มคุณภาพของโมเดล
— การ Deploy ที่ผู้ใช้สามารถปรัป Style ของรูปได้

Deployment

การ Deployment ของเราจะทำเป็น API ในการ Generate รูป
จากนั้นก็ส่งกลับผ่าน HTTP Request เพื่อนำไปแสดงผลที่ฝั่ง Front-End
เพราะเวลาเอาไปใช้งานต่อจะง่ายกว่าถ้าใช้เป็น API
วันนี้ผมจะใช้ AWS + Docker ในการ Deploy API เนื่องด้วยความง่าย

— — — — — — — — -Api(fastapi) — — — — — — — -
Main API : AWS EC2 instance Freetier + Docker
: http://54.251.20.58/api
Middle Man API : Heroku : https://api-aws-ganime.herokuapp.com/api
(Heroku เป็นตัวกลาง เพราะไม่สามารถ Request http: ฝั่ง Client ได้)
จริงๆแล้วเราสามารถต่อ Domain Name ได้ง่ายๆด้วย AWS แต่ผมแค่ไม่ได้ทำ

วิธีการ Deploy:
Main API : EC2 x Docker x FastAPI:
https://dev.to/theinfosecguy/how-to-deploy-a-fastapi-application-using-docker-on-aws-4m61
Middle Man API : Heroku FastAPI
https://towardsdatascience.com/how-to-deploy-your-fastapi-app-on-heroku-for-free-8d4271a4ab9
— — — — — — — — App — — — — — — -
HTML x CSS x Axios: Source Code
https://github.com/HRNPH/GANime-FullBody/tree/main/docs
Docker Images สำหรับใครที่ต้องการ Deploy API ตัวเอง
https://gallery.ecr.aws/z1f5v2y8/ganime-fullbody/model0

ทดลองใช้งาน:

https://hrnph.github.io/GANime-FullBody/

คำขอบคุณ — AI Builders

แม้ว่าจะเป็นคนธรรมดาจากโรงเรียนธรรมดา ไม่ได้เก่งอะไรเป็นพิเศษ
ผมที่รักการเขียนโปรแกรม ได้เรียนรู้ด้วยตัวเองมาตลอดในโลกแคบๆ
การที่ผมได้โอกาสเข้าค่าย AI Builders ทำให้ผมได้รับประสบการณ์ที่ไม่คิดว่าจะได้
ได้รับโอกาสในการฝันสิ่งที่ไม่เคยฝัน ได้มีโอกาสเจอกับคนที่ไม่คิดว่าชีวิตนี้จะเจอ
ได้เป็นเพื่อนกับคนเก่งมากมาย ได้มีเป้าหมายให้ไล่ตาม ได้มีคนให้ปรึกษา
ขอบคุณทุกๆคนที่คอยช่วยเหลือผมตลอดมา เพื่อนๆทุกคนที่เข้ามาพูดคุยกัน
การแลกเปลี่ยนความรู้ทุกครั้งนั้น ช่วยให้ผมเก่งขึ้นได้ และช่วยเติมส่วนที่ผมขาด

ขอขอบคุณ Mentor ของกลุ่มผม พี่ P และพี่ Oat ที่คอยช่วยผมตลอดโครงการ
พี่ KK ที่ช่วยปูพื้นฐานให้ผม ช่วย Debug โค้ด ช่วยให้คำปรึกษา ช่วยเสนอไอเดีย
ขอขอบคุณทุก Random Lecture ใน Discord ที่ผมได้มีโอกาสเข้าไปฟัง
สัมนาบน Wonder ที่ไม่มีเกียรติบัตรให้ แต่ได้ความรู้ราคาแพงที่ผมหาฟังไม่ได้
ในทุกๆครั้งผมได้เรียนรู้อะไรใหม่ๆเพิ่มเติมเสมอ

สุดท้ายนี้ขอขอบคุณพี่ๆ Staff เบื้องหลังที่ทำให้ค่ายดำเนินไปได้ด้วยดี
ขอขอบคุณพี่ชาริน และอาจารย์ทุกๆท่านที่เห็นความสำคัญ และยังจัดค่ายนี้ต่อไป
พี่ Makuda ที่นั่งเฝ้า Server AWS และ Support ให้ทั้งวันทั้งคืน

ในช่วงเวลา 10 สัปดาห์มานี้เต็มไปด้วยความทรงจำล้ำค่า มีอะไรเกิดขึ้นมากมาย
ผมได้พูดคุยกับใครหลายคน และเห็นความพยายามมากมายในค่ายแห่งนี้มาตลอด
ไม่ว่าจะเป็นนักเรียนที่อดหลับอดนอน พยายามให้งานตัวเองสำเร็จ
Mentor และ TA ที่สละเวลามาช่วยเหลือแม้จะไม่ค่อยว่าง
มีคนที่พยายามอย่างหนัก และทำได้ดียิ่งกว่าผมอยู่เต็มไปหมด

ตัวผมจึงเชื่ออย่างสุดหัวใจ ว่าโครงการนี้จะดีขึ้นเรื่อยๆ มีคุณภาพขึ้นเรื่อยๆ
และผลิตผลงานที่เป็นประโยชน์ออกมาในทุกๆปี
ขอขอบคุณ Sponsor ทุกท่านที่ให้การสนับสนุนโครงการดีๆแบบนี้
ผมหวังเป็นอย่างยิ่งว่าโครงการ จะได้รับการสนับสนุนมากขึ้นอีกในปีต่อๆไป

a program for kids who want to build good AI
Sponsor ของโครงการในปี 2022

ภาคผนวก — Code & Social Link

Github Project :
https://github.com/HRNPH/GANime-FullBody
Deployment API Code:
https://github.com/HRNPH/aws_deploy_ganime_fullbody

Social Link

Github : https://github.com/HRNPH
Youtube : HRN
Facebook: HRNPH

Credit — Gans model implementation CODE

AladdinPersson: https://www.youtube.com/c/AladdinPersson

ขาสวยChan GANGirl: Image For Thumbnail

--

--