ChatGPT火了,我連夜詳解AIGC原理,並實戰生成動漫頭像

語言: CN / TW / HK

theme: devui-blue

一、AIGC:人工智慧的新時代

AIGC可能會是人工智慧的下一個時代。儘管很多人還不知道AIGC是什麼。

當還有大批人宣揚所謂人工智慧、元宇宙都是概念,並且捂緊了口袋裡的兩百塊錢的時候,人工智慧行業發生了幾件小事。

首先,由人工智慧生成的一幅油畫作品《太空歌劇院》,獲得了藝術博覽會的冠軍。

有人感覺這有什麼?各種比賽多了去了,不就是獲個獎嗎?

可是這次不一樣,這是一幅油畫作品。在此之前,好的油畫只能由人工繪製。但是現在人工智慧也可以繪製了,而且還拿了冠軍。

很多人類藝術家仰天長嘆:“祖師爺啊,我這代人,在目睹藝術死亡!

上一次藝術家們發出這樣的感慨,還是1839年,那時照相機問世了。

隨後,ChatGPT橫空出世。它真正做到了和人類“對答如流”。

它也可以做數學題、創作詩歌、寫小說,甚至也能寫程式碼、改bug。

再說一個震驚的報道:由ChatGPT生成的論文,拿下了全班的最高分。導師找到學生,說他上交的論文,段落簡潔、舉例恰當、論據嚴謹,甚至引經據典,古今中外,無所不通,教授不敢相信。學生瑟瑟發抖,他說,這是AI生成的,我只是想應付一下作業

另外,美國89%的大學生都在用ChatGPT做作業。以色列總統在週三發表了一個演講,內容也是由人工智慧寫的。

現在全球都在討論,這類人工智慧技術,看似是帶來了巨大的商業價值,實則可能會給人類帶來嚴重的打擊。

這項技術就是AIGC(AI-Generated Content),翻譯成中文就是:人工智慧生成內容

二、AIGC實戰:智慧生成動漫頭像

其實,利用人工智慧生成內容資源,很早就有了。記得有一年的雙十一購物節,上萬商家的廣告圖就是人工智慧生成的。只是現在的資料、演算法、硬體,這三個條件跟上了,這才讓它大放異彩,全民可用。

下面,我就以人工智慧生成動漫頭像為例,採用TensorFlow框架,從頭到尾給大家講一下AIGC的全過程。從原理到實現都很詳細,自己搭建,不調API,最後還帶專案原始碼的那種

2.1 自動生成的意義

那位問了,自動生成內容有什麼好處?我的天啊,省事省力省錢吶!

下圖是一個遊戲中的海洋怪物。這便是人工智慧生成的。

這個大型遊戲叫《無人深空(No Man's Sky)》。號稱有1840億顆不同的星球,每個星球都有形態各異的怪物。這遊戲玩著得多爽啊?簡直就是視覺震撼吶。這些怪物要是人工來做,得招聘多少團隊,得花費多少時間?

用人工智慧生成的話,你可以像去網咖一樣,跟老闆說:嗨,多開幾臺機子

當然,下面我要做的,沒有上面那樣地絢麗,甚至很原始。

但是過程類似,原理一致。效果就是AI生成動漫頭像:

2.2 自動生成的原理

AIGC的原理,用中國古話可以一語概括,那就是:讀書破萬卷,下筆如有神

以生成貓咪的照片來舉例子,基本上AIGC的套路是下面這樣的:

首先,程式會設計兩個角色。一個叫生成器,一個叫鑑別器。

為了便於理解,我們稱呼生成器為藝術家,稱鑑別器為評論家。

藝術家負責生產內容,也就是畫貓。不要覺得擁有藝術家頭銜就很了不起,他可能和你一樣,畫不好。但是,就算亂畫,也得畫。於是,他就畫啊畫啊畫。

評論家呢,相比藝術家就負責一些了。他首先調研了大量貓的照片。他知道了貓的特點,有倆眼睛,有斑紋,有鬍鬚。這些特徵,他門兒清。

下面有意思的就來了

藝術家這時還啥也不懂,隨便畫一筆,然後交給評論家,說畫好了。評論家拿旁光一看,瞬間就給否了。還給出一些意見,比如連輪廓都沒有。

藝術家一聽,你要輪廓那我就畫個輪廓。他加了個輪廓,又交了上去。評論家正眼一看,又給否了。不過,他還是給出一些意見,比如沒有鬍鬚。

就這樣,這倆人經過成千上萬次的友好磋商(評論家幸好是機器,不然心態崩了)。到後來,藝術家再拿來畫作,評論家會看好久,甚至拿出之前的照片挨個對照。最後他甚至還想詐一下藝術家,說你這是假的,藝術家說這次是真的。這時,評論家說好吧,我確實找不出問題了,我看也是真的

至此,劇終。

搞一個造假的,再搞一個驗假的。然後訓練。隨著訓練加深,生成器在生成逼真影象方面逐漸變強,而鑑別器在辨別真偽上逐漸變強。當鑑別器無法區分真實圖片和偽造圖片時,訓練過程達到平衡。

上面這一套操作叫“生成對抗網路(Generative Adversarial Networks)”,簡稱叫GAN。我感覺,這套流程有點損,叫“幹”沒毛病。

2.3 資料準備

鑑別器是需要學習資料學習的。因此,我準備了20000張這樣的動漫頭像。

這些資料來自公開資料集Anime-Face-Dataset。資料檔案不大,274MB。你很容易就可以下載下來。這裡面有60000多張圖片。我用我的電腦訓練了一下。200分鐘過去了,一個epoch(把這些資料走一遍)都還沒有結束。那……稍微有效果得半個月之後了。

鄉親們,我這裡是AI小作坊,幹不了大的。於是乎,我就取了20000張圖片,並且將尺寸縮小到56×56畫素,再並且將彩色改為黑白。這樣一來,效率馬上就提高了。2分鐘就可以訓練一圈。如此,我訓練500圈也就是不到一天的時間。這是可以承受的。

上面處理圖片的程式碼:

``` python import cv2

存放源圖片的資料夾

dir_path = "anime" all_files=os.listdir(dir_path)

迴圈裡面的每一個檔案

for j,res_f_name in enumerate(all_files): res_f_path = dir_path+"/"+res_f_name # 讀入單通道 img1 = cv2.imread(res_f_path, 0) # 重新定義尺寸為56 img2=cv2.resize(img1,(56,56),interpolation=cv2.INTER_NEAREST) # 轉存到face資料夾下 cv2.imwrite("face/"+res_f_name, img2) # 超過20000退出迴圈 if j > 20000: break ```

相信加上註釋後,還是通俗易懂的。

檔案準備好了。儘管維度降了,但看起來,這個辨識度還過得去。

下一步要轉為TensorFlow格式化的資料集。

``` python from PIL import Image import pathlib import numpy as np

將圖片檔案轉為陣列

dir_path = "face" data_dir = pathlib.Path(dir_path) imgs = list(data_dir.glob('*.jpg')) img_arr = [] for img in imgs: img = Image.open(str(img)) img_arr.append(np.array(img)) train_images = np.array(img_arr) nums = train_images.shape[0]

train_images = train_images.reshape(nums, 56, 56, 1).astype('float32')

歸一化

train_images = (train_images - 127.5) / 127.5

轉為tensor格式

train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(nums).batch(256) ```

我很想說一下資料形態的變化過程。因為這和後續的神經網路結構有關聯。

首先,我們的圖片是56×56畫素,單通道。所以,圖片的資料陣列img_arr的形狀是(20000, 56, 56)。也就是說有20000組56×56的陣列。這裡面的數是int型的,取值為0到255,表示從純黑到純白。

((20000, 56, 56), array([[ 0, 0, 0, 0, 0, …… 0], [ 18, 18, 126, 136, 175, …… 0], [ 0, 0, 253, 253, 0, …… 0]], dtype=uint8))

然後用reshape做一個升維,並且用astype('float32')做一個浮點轉化。

升維的目的,是把每一個畫素點單獨提出來。因為每一個畫素點都需要作為學習和判斷的依據。浮點轉化則是為了提高精確度。

到這一步train_images的形狀變為(20000, 56, 56, 1)

((20000, 56, 56, 1), array([[ [0.], [0.], [0.], [0.], [0.], …… [0.]], [ [18.], [18.], [126.], [136.], [175.], …… [0.]], [ [0.], [0.], [253.], [253.], [0.], …… [0.]]], dtype=float32))

接著,進行一個神奇的操作。執行了(train_images-127.5)/127.5這一步。這一步是什麼作用呢?我們知道,色值最大是255,那麼他的一半就是127.5。可以看出來,上一步操作就是把資料的區間格式化到[-1,1]之間。

如果你足夠敏感的話,或許已經猜到。這是要使用tanh,也就是雙曲正切作為啟用函式。

這個函式的輸出範圍也是在-1到1之間。也就是說,經過一系列計算,它最終會輸出-1到1之間的數值。這個數值我們反向轉化回去,也就是乘以127.5然後加上127.5,那就是AI生成畫素的色值。

2.4 生成器

首先我們來建立一個生成器。用於生成動漫頭像的圖片。

``` python def make_generator_model(): model = tf.keras.Sequential() model.add(layers.Dense(77256, use_bias=False, input_shape=(160,))) model.add(layers.BatchNormalization()) model.add(layers.LeakyReLU())

model.add(layers.Reshape((7, 7, 256)))
assert model.output_shape == (None, 7, 7, 256)
model.add(layers.Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False))
assert model.output_shape == (None, 7, 7, 128)
model.add(layers.BatchNormalization())
model.add(layers.LeakyReLU())
……
model.add(layers.Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh'))
assert model.output_shape == (None, 56, 56, 1)

return model

生成一個試試

generator = make_generator_model() noise = tf.random.normal([1, 160]) generated_image = generator(noise, training=False) ```

因為我最終會放出全部原始碼,所以這個地方省略了幾層相似的神經網路。

從結構上看,輸入層是大小為160的一維噪點資料。然後通過Conv2DTranspose實現上取樣,一層傳遞一層,生成變化的影象。最終到輸出層,通過tanh啟用函式,輸出56×56組資料。這將會是我們要的畫素點。

如果輸出一下,生成器生成的圖片。是下面這個樣子。

這沒錯,一開始生成的影象,就是隨機的畫素噪點。它只有一個確定項,那就是56×56畫素的尺寸。

這就可以了。它已經通過複雜的神經網路,生成圖片了。這個生成器有腦細胞,但剛出生,啥也不懂。

這就像是藝術家第一步能繪製線條了。如果想要畫好貓,那就得找評論家多去溝通。

2.5 鑑別器

我們來建立一個鑑別器。用於判斷一張動漫頭像是不是真的。

``` python def make_discriminator_model(): model = tf.keras.Sequential() model.add(layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same', input_shape=[56, 56, 1])) model.add(layers.LeakyReLU()) model.add(layers.Dropout(0.3))

model.add(layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same'))
model.add(layers.LeakyReLU())
model.add(layers.Dropout(0.3))

model.add(layers.Flatten())
model.add(layers.Dense(1))

return model

鑑別上一個生成的噪點圖片generated_image試試

discriminator = make_discriminator_model() decision = discriminator(generated_image) ```

我們來看一下這個模型。它的輸入形狀是(56, 56, 1)。也就是前期準備的資料集的形狀。它的輸出形狀是(1),表示鑑別的結果。中間是兩層卷積,用於把輸入向輸出聚攏。採用的是LeakyReLU啟用函式。

我們把生成器生成的那個噪點圖,鑑別一下,看看啥效果。

tf.Tensor([[0.00207942]], shape=(1, 1), dtype=float32)

看這個輸出結果,數值極小,表示可能性極低。

我們只是建立了一個空的模型。並沒有訓練。它這時就判斷出了不是動漫頭像。倒不是因為它智慧,而是它看啥都是假的。它現在也是個小白。

下面就該訓練訓練了。

2.6 訓練資料

開練!GAN!

``` python cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)

def discriminator_loss(real_output, fake_output): real_loss = cross_entropy(tf.ones_like(real_output), real_output) fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output) total_loss = real_loss + fake_loss return total_loss

def generator_loss(fake_output): return cross_entropy(tf.ones_like(fake_output), fake_output)

…… @tf.function def train_step(images):

noise = tf.random.normal([BATCH_SIZE, noise_dim])

with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
    generated_images = generator(noise, training=True)

    real_output = discriminator(images, training=True)
    fake_output = discriminator(generated_images, training=True)

    gen_loss = generator_loss(fake_output)
    disc_loss = discriminator_loss(real_output, fake_output)

gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)

generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))

for epoch in range(500): for image_batch in dataset: train_step(image_batch) ```

同樣,我還是隻放出了部分關鍵程式碼。不然影響你的閱讀。最後我會開源這個專案,不要著急。

我們來分析原理,一定要反覆看,精彩和燒腦程度堪比《三國演義》。我連圖片都不敢加,怕打斷你的思緒。

首先看損失函式。

演算法訓練的一個途徑,就是讓損失函式的值越變越小。損失函式表示差距,預測的差距和實際差距縮小,表示預測變準。

先看一下生成器的損失函式。位置在程式碼中的generator_loss部分。它返回兩個資料之間的差距。第一個數是造假的結果fake_output,這個結果是鑑別器給的。另一個數據是標準的成功結果。隨著訓練的進行,演算法框架會讓這個函式的值往小了變。那其實就是讓生成器預測出來的資料,同鑑別器判斷出來的結果,兩者之間的差距變得越來越小。這一番操作,也就是讓框架留意,如果整體趨勢是生成器欺騙鑑別器的能力增強,那就加分。

再看鑑別器的損失函式。也就是程式碼中的discriminator_loss函式。它這裡稍微複雜一些。我們看到它的值是real_lossfake_loss,是兩項損失值的總和。real_lossreal_output和標準答案的差距。fake_lossfake_output和標準答案的差距。

那這兩個值又是怎麼來的呢?得去train_step函式裡看。real_output是鑑別器對訓練資料的判斷。fake_loss是鑑別器對生成器造假結果的判斷。看到這裡,我感嘆人工智慧的心機之重。它什麼都要。

隨著大量學習資料的迴圈,它告訴人工智慧框架,它要鍛鍊自己對現有學習材料鑑別的能力。如果自己猜對了學習資料,也就是那20000張動漫頭像。請提醒我,我要調整自己的見識,修改內部引數。程式碼中定義的training=True,意思就是可隨著訓練自動調節引數。

同時,伴著它學習現有資料的過程中,它還要實踐。它還要去判斷生成器是不是造假了。它也告訴框架,我要以我現在學到的鑑別能力,去判斷那小子造的圖假不假。

因為人工智慧要想辦法讓損失函式變小。因此得讓fake_loss的值變小,才能保證discriminator_loss整體變小。於是,框架又去找生成器。告訴它,鑑別器又學習了一批新知識,現在人家識別造假的能力增強了。不過,我可以偷偷地告訴你,它學了這個還有那個。這麼一來,生成器造假的本領,也增強了。

如此迴圈往復。框架相當於一個“挑唆者”。一邊讓鑑別器提高鑑別能力,一邊也告訴生成器如何實現更高階的造假。最終,世間所有的知識,兩方全部都學到了。鑑別器再也沒有新的知識可以學習。生成器的造假,鑑別器全部認可,也不需要再有新的造假方案。所有防偽知識全透明

這時AIGC就成功了。

2.7 自動生成

我對20000張動漫圖片訓練了500輪。每一輪都列印一個九宮格的大頭貼。最終我們可以看到這500輪的演變效果。這張圖大約25秒,只播放一遍(如果放完了,拖出來再看),需要耐心看。

從動態圖看,整體趨勢是往畫面更清晰的方向發展的。

動圖比較快,我放上一張靜態圖。這完全是由人工智慧生成的圖片。

生成的程式碼很簡單。

``` python

載入訓練模型

if os.path.exists(checkpoint_dir+"/checkpoint"): checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))

生成噪點作為輸入

test_input = tf.random.normal([1, 160])

交給生成器批量生成

predictions = generator(test_input, training=False)

取出一張結果

img_arr = predictions[0][:, :, 0]

將結果復原成圖片畫素色值資料

img_arr = img_arr* 127.5 + 127.5 ```

這是20000張圖,500輪訓練的效果。如果是百萬張圖片,幾千輪訓練呢?完全模擬很簡單。

專案開源地址:http://gitee.com/bigcool/gan_face

三、我們對AIGC該有的態度

AIGC的火爆出圈,引起全球的強烈討論。很多地方甚至打算立法,禁止學生使用它做作業。

雖然我說了這麼多。可能直到現在,依然有人覺得這是噱頭:我的工作這麼高階,是有靈魂的工作,人工智慧寫文章能比我通順?它還寫程式碼?它懂邏輯嗎?

國外有一個IT老哥叫David Gewirtz。他從1982年開始就寫程式碼,幹了40多年,也在蘋果公司待過。他以為用ChatGPT寫程式碼不會有啥驚喜。直到出現結果,卻嚇了他一大跳。

他的需求是給它老婆寫一個網站的外掛,用於挑選顧客,並滾動顧客的名字展示。這個需要幾天完成的工作,ChatGPT很快就完成了。而且程式碼純粹簡潔,極其規範。它還告訴你該操作哪個檔案,該如何部署

現階段的人工智慧,可能沒有自己的思考,但是它有自己的計算。

你會寫文章,因為你讀過300多本書,並且記住了裡面20%的內容。這些讓你引以為傲。但是人工智慧,它讀過人類歷史上出現過的所有文獻,只要硬碟夠,它全部都能記住。而且它還不停對這些內容做分析、加工、整理:這裡和這裡有關聯,這裡和那裡都是在介紹橙子的營養成分。它通過計算,讓一切知識發生互聯互通。

當有人向人工智慧表示人類的擔憂時,人工智慧也給出了自己的回答。

我比較贊同它的觀點。

抱有其他觀點的人,主要擔心有了人工智慧,人類就會變得不動腦子了。時間長就廢了。

我覺得,這些都是工具。相機出來的時候,也是被畫家抵制,因為成像太簡單了。現在想想,太簡單有問題嗎?沒有!同樣的還有計算器之於算盤,打字之於手寫。甚至TensorFlow 2.0出來時,也被1.0的使用者抵制。他們說開發太簡單了,這讓開發者根本接觸不到底層。殊不知,1.0出來的時候,那些寫組合語言的開發者也想,他們墮落了,居然不操作暫存器。

其實,我感覺這些擔心是多餘的。每個時代都有會屬於自己時代的產物。就像現在我們不用毛筆寫字了,但是我們的祖先也沒有敲過鍵盤呀!可能下一個時代的人,連鍵盤也不敲了。

我是掘金@TF男孩,一位程式設計表演藝術家。