AI說書 - 從0開始 - 529 | Stable Diffusion 之 Keras 實現 (DownSample)

更新 發佈閱讀 19 分鐘

我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。


想像你正在改進一張「雜訊重重」的圖像,如果一次修改過多,可能會偏離主題,因此,你必須將工作分成小步驟來進行,每完成一個小且漸進的步驟後,你都需要重新檢查主題,以確保你的創意符合主題,你還決定縮小圖像,彷彿在總結符合主題的主要特徵。這種逐步且穩定的處理方式使過程「穩定」下來。透過保留圖像中最具代表性的特徵來縮小圖像,並以這些方法來處理圖像,你就在進行「擴散」的過程,其數學表示方式為:It+1 = It - Alpha * (It - Desired_State)


Keras 實現 Stable Diffusion 的 Down-Sampling 部分為:

class DiffusionModelV2(keras.Model):
def __init__(self, img_height, img_width, max_text_length, name = None, download_weights = True):
context = keras.layers.Input((max_text_length, 1024))
t_embed_input = keras.layers.Input((320,))
latent = keras.layers.Input((img_height // 8, img_width // 8, 4))

t_emb = keras.layers.Dense(1280)(t_embed_input)
t_emb = keras.layers.Activation("swish")(t_emb)
t_emb = keras.layers.Dense(1280)(t_emb)

# Downsampling Flow

outputs = []
x = PaddedConv2D(320, kernel_size = 3, padding = 1)(latent)
outputs.append(x)

for _ in range(2):
x = ResBlock(320)([x, t_emb])
x = SpatialTransformer(5, 64, fully_connected = True)([x, context])
outputs.append(x)
x = PaddedConv2D(320, 3, strides = 2, padding = 1)(x) # Downsample 2x
outputs.append(x)

for _ in range(2):
x = ResBlock(640)([x, t_emb])
x = SpatialTransformer(10, 64, fully_connected = True)([x, context])
outputs.append(x)
x = PaddedConv2D(640, 3, strides = 2, padding = 1)(x) # Downsample 2x
outputs.append(x)

for _ in range(2):
x = ResBlock(1280)([x, t_emb])
x = SpatialTransformer(20, 64, fully_connected = True)([x, context])
outputs.append(x)
x = PaddedConv2D(1280, 3, strides = 2, padding = 1)(x) # Downsample 2x
outputs.append(x)

for _ in range(2):
x = ResBlock(1280)([x, t_emb])
outputs.append(x)

# Middle Flow

x = ResBlock(1280)([x, t_emb])
x = SpatialTransformer(20, 64, fully_connected = True)([x, context])
x = ResBlock(1280)([x, t_emb])

# Upsampling Flow

for _ in range(3):
x = keras.layers.Concatenate()([x, outputs.pop()])
x = ResBlock(1280)([x, t_emb])
x = Upsample(1280)(x)

for _ in range(3):
x = keras.layers.Concatenate()([x, outputs.pop()])
x = ResBlock(1280)([x, t_emb])
x = SpatialTransformer(20, 64, fully_connected = True)([x, context])
x = Upsample(1280)(x)

for _ in range(3):
x = keras.layers.Concatenate()([x, outputs.pop()])
x = ResBlock(640)([x, t_emb])
x = SpatialTransformer(10, 64, fully_connected = True)([x, context])
x = Upsample(640)(x)

for _ in range(3):
x = keras.layers.Concatenate()([x, outputs.pop()])
x = ResBlock(320)([x, t_emb])
x = SpatialTransformer(5, 64, fully_connected = True)([x, context])

# Exit Flow

x = keras.layers.GroupNormalization(epsilon = 1e-5)(x)
x = keras.layers.Activation("swish")(x)
output = PaddedConv2D(4, kernel_size = 3, padding = 1)(x)


當中關鍵區塊程式的源碼為:

class ResBlock(keras.layers.Layer):
def __init__(self, output_dim, **kwargs):
super().__init__(**kwargs)
self.output_dim = output_dim
self.entry_flow = [keras.layers.GroupNormalization(epsilon = 1e-5),
keras.layers.Activation("swish"),
PaddedConv2D(output_dim, 3, padding = 1),]
self.embedding_flow = [keras.layers.Activation("swish"),
keras.layers.Dense(output_dim),]
self.exit_flow = [keras.layers.GroupNormalization(epsilon = 1e-5),
keras.layers.Activation("swish"),
PaddedConv2D(output_dim, 3, padding = 1),]

def call(self, inputs):
inputs, embeddings = inputs
x = inputs
for layer in self.entry_flow:
x = layer(x)
for layer in self.embedding_flow:
embeddings = layer(embeddings)
x = x + embeddings[:, None, None] # Channel Dim is 3 in Keras
for layer in self.exit_flow:
x = layer(x)
return x + self.residual_projection(inputs)




class SpatialTransformer(keras.layers.Layer):
def __init__(self, num_heads, head_size, fully_connected = False, **kwargs):
super().__init__(**kwargs)
self.norm = keras.layers.GroupNormalization(epsilon = 1e-5)
channels = num_heads * head_size
if fully_connected:
self.proj1 = keras.layers.Dense(num_heads * head_size)
else:
self.proj1 = PaddedConv2D(num_heads * head_size, 1)
self.transformer_block = BasicTransformerBlock(channels, num_heads, head_size)
if fully_connected:
self.proj2 = keras.layers.Dense(channels)
else:
self.proj2 = PaddedConv2D(channels, 1)

def call(self, inputs):
inputs, context = inputs
_, h, w, c = inputs.shape
x = self.norm(inputs)
x = self.proj1(x)
x = ops.reshape(x, (-1, h * w, c))
x = self.transformer_block([x, context]) # 注意 x 的最後維度和 context 最後維度可以不同,因為 Attention 機制一進去會先做 Dense
x = ops.reshape(x, (-1, h, w, c))
return self.proj2(x) + inputs




class BasicTransformerBlock(keras.layers.Layer):
def __init__(self, dim, num_heads, head_size, **kwargs):
super().__init__(**kwargs)
self.norm1 = keras.layers.LayerNormalization(epsilon = 1e-5)
self.attn1 = CrossAttention(num_heads, head_size)
self.norm2 = keras.layers.LayerNormalization(epsilon = 1e-5)
self.attn2 = CrossAttention(num_heads, head_size)
self.norm3 = keras.layers.LayerNormalization(epsilon = 1e-5)
self.geglu = GEGLU(dim * 4)
self.dense = keras.layers.Dense(dim)

def call(self, inputs):
inputs, context = inputs
x = self.attn1(self.norm1(inputs), context = None) + inputs
x = self.attn2(self.norm2(x), context = context) + x
return self.dense(self.geglu(self.norm3(x))) + x




class CrossAttention(keras.layers.Layer):
def __init__(self, num_heads, head_size, **kwargs):
super().__init__(**kwargs)
self.to_q = keras.layers.Dense(num_heads * head_size, use_bias = False)
self.to_k = keras.layers.Dense(num_heads * head_size, use_bias = False)
self.to_v = keras.layers.Dense(num_heads * head_size, use_bias = False)
self.scale = head_size**-0.5
self.num_heads = num_heads
self.head_size = head_size
self.out_proj = keras.layers.Dense(num_heads * head_size)

def call(self, inputs, context = None):
if context is None:
context = inputs
q, k, v = self.to_q(inputs), self.to_k(context), self.to_v(context)
q = ops.reshape(q, (-1, inputs.shape[1], self.num_heads, self.head_size))
k = ops.reshape(k, (-1, context.shape[1], self.num_heads, self.head_size))
v = ops.reshape(v, (-1, context.shape[1], self.num_heads, self.head_size))

q = ops.transpose(q, (0, 2, 1, 3)) # (bs, num_heads, time, head_size) # 注意這裡的 time 是圖片的 h x w
k = ops.transpose(k, (0, 2, 3, 1)) # (bs, num_heads, head_size, time) # 注意這裡的 time 是 context 長 ; 不一定等於 h x w
v = ops.transpose(v, (0, 2, 1, 3)) # (bs, num_heads, time, head_size) # 注意這裡的 time 是 context 長 ; 不一定等於 h x w

score = td_dot(q, k) * self.scale
weights = keras.activations.softmax(score) # (bs, num_heads, time, time)
attn = td_dot(weights, v)
attn = ops.transpose(attn, (0, 2, 1, 3)) # (bs, time, num_heads, head_size)
out = ops.reshape(attn, (-1, inputs.shape[1], self.num_heads * self.head_size))
return self.out_proj(out)


解析如下:

  • embeddings[:, None, None]
    1. 形狀從 (batch_size, embedding_dim) 變為 (batch_size, 1, 1, embedding_dim)
    2. 第一個 None 在原來的第 1 維插入一個新維度
    3. 第二個 None 在原來的第 2 維插入一個新維度
留言
avatar-img
Learn AI 不 BI
247會員
1.1K內容數
這裡將提供: AI、Machine Learning、Deep Learning、Reinforcement Learning、Probabilistic Graphical Model的讀書筆記與演算法介紹,一起在未來AI的世界擁抱AI技術,不BI。
Learn AI 不 BI的其他內容
2025/10/19
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 Keras 實現 Stable Diffusion 的隨機初始圖片部分為: def _get_initial_diffusion_noise(self, batch_
2025/10/19
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 Keras 實現 Stable Diffusion 的隨機初始圖片部分為: def _get_initial_diffusion_noise(self, batch_
2025/10/18
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 Keras 實現 Stable Diffusion 的 Encoder 部分為: class TextEncoder(keras.Model): def __ini
Thumbnail
2025/10/18
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 Keras 實現 Stable Diffusion 的 Encoder 部分為: class TextEncoder(keras.Model): def __ini
Thumbnail
2025/10/17
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 Keras 實現 Stable Diffusion 的流程為: Text Embedding Random Image Creation Stable Diffu
Thumbnail
2025/10/17
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 Keras 實現 Stable Diffusion 的流程為: Text Embedding Random Image Creation Stable Diffu
Thumbnail
看更多
你可能也想看
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
別讓你的房子,變成家中最大的「閒置資產」 作為一名服務高淨值客戶的私人銀行顧問,我每天的任務只有一個:幫客戶「讓錢滾動」。然而,當我觀察身旁許多同樣育有子女的朋友們,即便他們多半已是職場上的中高階主管,表面上看似光鮮亮麗,有房有車;但實際上,大家都是典型的「夾心世代」。每個月薪水一入帳,扣掉沉重的
Thumbnail
別讓你的房子,變成家中最大的「閒置資產」 作為一名服務高淨值客戶的私人銀行顧問,我每天的任務只有一個:幫客戶「讓錢滾動」。然而,當我觀察身旁許多同樣育有子女的朋友們,即便他們多半已是職場上的中高階主管,表面上看似光鮮亮麗,有房有車;但實際上,大家都是典型的「夾心世代」。每個月薪水一入帳,扣掉沉重的
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 目前我們已經有資料集在 AI說書 - 從0開始 - 103 ,必要的清理函數在 AI說書 - 從0開始 - 104 ,現在把它們湊在一起,如下: # load Eng
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 目前我們已經有資料集在 AI說書 - 從0開始 - 103 ,必要的清理函數在 AI說書 - 從0開始 - 104 ,現在把它們湊在一起,如下: # load Eng
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 先做個總回顧: Transformer 架構總覽:AI說書 - 從0開始 - 39 Attention 意圖說明:AI說書 - 從0開始 - 40 Transfo
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 先做個總回顧: Transformer 架構總覽:AI說書 - 從0開始 - 39 Attention 意圖說明:AI說書 - 從0開始 - 40 Transfo
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 目前我們已經完成: Single-Head Attention 數學說明:AI說書 - 從0開始 - 52 Multi-Head Attention 數學說明:
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 目前我們已經完成: Single-Head Attention 數學說明:AI說書 - 從0開始 - 52 Multi-Head Attention 數學說明:
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 目前我們已經完成: Single-Head Attention 數學說明:AI說書 - 從0開始 - 52 Multi-Head Attention 數學說明:AI
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 目前我們已經完成: Single-Head Attention 數學說明:AI說書 - 從0開始 - 52 Multi-Head Attention 數學說明:AI
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 目前我們已經完成: Single-Head Attention 數學說明:AI說書 - 從0開始 - 52 Multi-Head Attention 數學說明:AI
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 目前我們已經完成: Single-Head Attention 數學說明:AI說書 - 從0開始 - 52 Multi-Head Attention 數學說明:AI
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 目前我們已經完成: Single-Head Attention 數學說明:AI說書 - 從0開始 - 52 Multi-Head Attention 數學說明:AI
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 目前我們已經完成: Single-Head Attention 數學說明:AI說書 - 從0開始 - 52 Multi-Head Attention 數學說明:AI
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 目前我們已經完成: Single-Head Attention 數學說明:AI說書 - 從0開始 - 52 Multi-Head Attention 數學說明:AI
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 目前我們已經完成: Single-Head Attention 數學說明:AI說書 - 從0開始 - 52 Multi-Head Attention 數學說明:AI
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 目前我們已經完成: Single-Head Attention 數學說明:AI說書 - 從0開始 - 52 Multi-Head Attention 數學說明:AI
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 目前我們已經完成: Single-Head Attention 數學說明:AI說書 - 從0開始 - 52 Multi-Head Attention 數學說明:AI
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 目前我們已經完成: Single-Head Attention 數學說明:AI說書 - 從0開始 - 52 Multi-Head Attention 數學說明:AI
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 目前我們已經完成: Single-Head Attention 數學說明:AI說書 - 從0開始 - 52 Multi-Head Attention 數學說明:AI
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 延續AI說書 - 從0開始 - 25示範了ChatGPT程式的能力,現在我們繼續做下去。 AI說書 - 從0開始 - 25在步驟7:Plot the confusio
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 延續AI說書 - 從0開始 - 25示範了ChatGPT程式的能力,現在我們繼續做下去。 AI說書 - 從0開始 - 25在步驟7:Plot the confusio
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News