在 Mac 玩 Core ML Stable Diffusion 的小心得

2023/04/10 更新:
現在有很多現成的 App 可以用 AI 畫圖了,建議不要再用本篇文章提到的方法,比較簡單也更快速。

原本想寫一篇如何在 Apple Silicon 的 Mac 玩 Core ML Stable Diffusion 的文章,後來發現原本文件已經很清楚了,所以覺得應該不用再贅述,改成寫一些使用的心得,還有一些小技巧好了。

先講重點:雖然 Apple 在 GitHub 上面寫跟我同規格的 M1 Ultra 48 GPU 版本可以 13 秒跑出結果,但是那是「畫圖部分」的跑分,在畫圖之前要先載入模型,然後還有 sampling 之類的動作也要花時間,而這些載入動作本身有可能比「畫圖」還要更耗時間,所以在本文我測了一下,然後整理了一些小技巧,可以讓執行速度快一點,也可以讓大家少走點彎路。

以下測試環境:

  • macOS 13.1 Beta 4(22C5059b)
  • Mac Studio(M1 Ultra 20 CPU,48 GPU,64GB RAM)
  • Stable Diffusion v1.4
  • Prompt 固定為 "a high quality photo of an astronaut riding a horse in space"
  • Seed 固定使用 13

 

在 macOS 13.1 以上執行

雖然 macOS 13.0.1 也可以執行,但是 Apple 有在 macOS 13.1 對 Stable Difussion 做最佳化。更重要的是在 macOS 13.0.1 是無法呼叫 GPU 的,生成的圖片會變成一整片灰色。只能給 CPU 跟 Apple 的神經網路引擎(Apple's Neural Engine,縮寫為 ANE)使用,效能差很多。

 

使用轉換好的 Checkpoints

不要浪費時間按照原本 GitHub 上面的教學手動轉換 model,因為需要很長的時間跟大量記憶體(在 M1 Ultra 我轉換 Stable Diffusion 2 Base 花了半小時以上,用了 40GB RAM),可以在這邊下載已經轉換好的 checkpoint。

可用的預先轉換 checkpoint 包含:

  • Stable Diffusion v1.4
  • Stable Diffusion v1.5
  • Stable Diffusion v2 base

下載需要 git-lfs,要另外用 homebrew 安裝。

 

下載完成之後會看到 original 跟 split_einsum 兩個版本,original 是 CPU 跟 GPU 用的,split_einsum 則可以給 ANE 使用。

每個資料夾底下又分為 packages 跟 compiled,packages 是給 python 用的,compiled 是給 swift 用的。

有些 model 會有 unet.mlpackage、unet_chunk1.mlpackage、unet_chunk2.mlpackage 三個檔案,在電腦上我們不需要把檔案分割以減少資源消耗,所以可以直接砍掉 unet_chunk1、unet_chunk2 減少佔用空間。

即使使用轉換好的 checkpoint,第一次使用的時候還是會先下載一堆東西,這是正常的。

 

Swift 的版本通常比較快

雖然原始文件寫用 Python 在 Mac 上比較快,但問題依然一樣,那不包含模型載入時間。實際使用 Python 時,因為每次模型載入後都還要在 runtime 轉換一次,導致載入效率非常差,反而用 Swift 因為少了一些轉換過程,所以跑起來比較快。

以下是一些測試結果,都是用預先轉換的 Stable Diffusion v1.4 checkpoint,包含模型載入時間,都是用 CPU 和 GPU 運算(測試發現在高階的 Mac 上面用 ANE 反而拖慢速度)

  • original + python: 51s
  • original + swift: 42s
  • split_einsum + python: 43s
  • split_einsum + swift: 28s

可以看到,用 Swift 版本快了約 10 秒,用 split_einsum 又比 original 快 10 秒。但上面這些測試這都是只跑一張圖的情形下,如果要跑多張圖的話請參考下一段。

 

使用 --image-count 指令產生大量圖片

如果你確定 prompt 沒問題的話,可以用 --image-count 指令批次產生圖片,這樣的好處是模型只需要載入一次。簡單來說,「用同一個指令畫一張圖,畫 100 次」vs「一個指令直接出 100 張圖」兩者相比,後者快上很多。

雖然上一段寫到 split_einsum 比較快,但那也是只有一張圖的情形下,若產生多張圖的話用 original 速度反而比較快。

 

不過 Apple 原本提供的 --image-count 指令有兩個小缺點:

第一個是除了第一張圖知道 seed 之外,後面的圖 seed 是用某個函數算出來的下一組偽隨機數,這導致我們很難在批次模式下,重新畫出一樣的圖片(除非你要整個批次執行都重跑)。第二點是它要把所有的圖片都畫完才會一次輸出,不能畫一張就先輸出一張。

我自己 fork 了一下這個 repo,然後加了一個可選的 --increment-seed 參數,可以解決這兩個問題。使用 --increment-seed 之後批次圖片的後續圖片都只是直接對 seed 數字 +1 而已,使得重繪單一圖片的難度大幅降低。並且加入這個參數之後,圖片也是畫一張就出一張,這樣方便我邊看輸出邊調整,若跑到一半發現不對勁,就直接停掉程式,不必等到全部跑完幾百張才發現「啊靠,prompt 下錯了」。實測這個參數對整體速度沒有顯著影響,不會拖累繪圖速度。

如果想要用 --increment-seed 可以 clone 我的 repo:hirakujira/ml-stable-diffusion at dev (github.com),用 dev branch 就有這個功能了。

 

關閉 Safety Checker 來加速

在 Apple 提供的程式碼裡面,預設是開啟 Safety Checker 的,這個東西可以防止使用者產生一些 NSFW 的內容。請注意,關閉 Safety Checker 僅是為了加速圖片繪製使用,請勿濫用這個功能。

Python 版本,請修改 pipeline.py,在 463 行左右新增下面最下面兩行內容:

 

Swift 版本啥都不用改,直接用 --disable-safety 指令就好了。

實測 Python 版本會快 4 秒鐘左右,Swift 加速比較不明顯,只快了 1 秒左右,但批次跑起來還是差蠻多的。關掉 Safety Checker 之後,可以刪除 safety_checker.mlpackage 或者 SafetyChecker.mlmodelc 檔案,節省空間。

部分 model 內建 safety checker,例如 Stable Diffusion v2 會忽略 NSFW 的 prompt,所以這招對它沒用。

 

隨機種子

--seed ${RANDOM} 指令會使用隨機種子,不必每次都在那邊調種子數字。如果嫌 --seed ${RANDOM} 給的數字範圍太小,可以用 --seed $(od -N 4 -t uL -An /dev/urandom | tr -d " ")

 

不同運算單元畫出的成果不同

即使使用同一個 seed 跟 prompt,用 GPU 跟 ANE 繪圖出來的結果會構圖接近,但是不同。

舉個例子,左邊用 CPU 跟 ANE,右邊用 CPU 跟 GPU:

 

最後

不用我說,你也知道,封面圖就是用 AI 畫出來的。

 

參考文章:Using Stable Diffusion with Core ML on Apple Silicon (huggingface.co)

3 則留言

  1. 「Winlng」的個人頭像
    Winlng

    請問 文中提到:
    「關掉 Safety Checker」 Python 版本,請修改 pipeline.py,在 463 行左右新增下面最下面兩行內容.

    找到系統裡有兩個 pipeline.py,但都找不到您畫面上的 def main(args),請問是否能幫我說明下,謝謝!

    1. 「dj」的個人頭像
      dj

      可以出一个如何把sd模型转换为checkpoints的教程吗

    2. 「Hiraku」的個人頭像
      Hiraku

      現在不推薦這樣用了,因為已經有很多更方便使用的 App

      這幾個是使用 CoreML 的:
      MochiDiffusion https://github.com/godly-devotion/MochiDiffusion
      Drawthings AI https://drawthings.ai/ (可設定強制使用 CoreML,也可不用,用 CoreML 繪圖速度會快 40%)

      這個是一般最常用的:
      https://github.com/AUTOMATIC1111/stable-diffusion-webui
      這個沒有 CoreML 加速,所以速度上沒上面兩個快,但是支援最多功能,更新也最勤

發佈留言