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
248會員
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
賽勒布倫尼科夫以流亡處境回望蘇聯電影導演帕拉贊諾夫的舞台作品,以十段寓言式殘篇,重新拼貼記憶、暴力與美學,並將審查、政治犯、戰爭陰影與「形式即政治」的劇場傳統推到台前。本文聚焦於《傳奇:帕拉贊諾夫的十段殘篇》的舞台美術、音樂與多重扮演策略,嘗試解析極權底下不可言說之事,將如何成為可被觀看的公共發聲。
Thumbnail
賽勒布倫尼科夫以流亡處境回望蘇聯電影導演帕拉贊諾夫的舞台作品,以十段寓言式殘篇,重新拼貼記憶、暴力與美學,並將審查、政治犯、戰爭陰影與「形式即政治」的劇場傳統推到台前。本文聚焦於《傳奇:帕拉贊諾夫的十段殘篇》的舞台美術、音樂與多重扮演策略,嘗試解析極權底下不可言說之事,將如何成為可被觀看的公共發聲。
Thumbnail
柏林劇團在 2026 北藝嚴選,再次帶來由布萊希特改編的經典劇目《三便士歌劇》(The Threepenny Opera),導演巴里・柯斯基以舞台結構與舞台調度,重新向「疏離」進行提問。本文將從觀眾慾望作為戲劇內核,藉由沉浸與疏離的辯證,解析此作如何再次照見觀眾自身的位置。
Thumbnail
柏林劇團在 2026 北藝嚴選,再次帶來由布萊希特改編的經典劇目《三便士歌劇》(The Threepenny Opera),導演巴里・柯斯基以舞台結構與舞台調度,重新向「疏離」進行提問。本文將從觀眾慾望作為戲劇內核,藉由沉浸與疏離的辯證,解析此作如何再次照見觀眾自身的位置。
Thumbnail
本文深入解析臺灣劇團「晃晃跨幅町」對易卜生經典劇作《海妲.蓋柏樂》的詮釋,從劇本歷史、聲響與舞臺設計,到演員的主體創作方法,探討此版本如何讓經典劇作在當代劇場語境下煥發新生,滿足現代觀眾的觀看慾望。
Thumbnail
本文深入解析臺灣劇團「晃晃跨幅町」對易卜生經典劇作《海妲.蓋柏樂》的詮釋,從劇本歷史、聲響與舞臺設計,到演員的主體創作方法,探討此版本如何讓經典劇作在當代劇場語境下煥發新生,滿足現代觀眾的觀看慾望。
Thumbnail
《轉轉生》為奈及利亞編舞家庫德斯.奧尼奎庫與 Q 舞團創作的當代舞蹈作品,融合舞蹈、音樂、時尚和視覺藝術,透過身體、服裝與群舞結構,回應殖民歷史、城市經驗與祖靈記憶的交錯。本文將從服裝設計、身體語彙與「輪迴」的「誕生—死亡—重生」結構出發,分析《轉轉生》如何以當代目光,形塑去殖民視角的奈及利亞歷史。
Thumbnail
《轉轉生》為奈及利亞編舞家庫德斯.奧尼奎庫與 Q 舞團創作的當代舞蹈作品,融合舞蹈、音樂、時尚和視覺藝術,透過身體、服裝與群舞結構,回應殖民歷史、城市經驗與祖靈記憶的交錯。本文將從服裝設計、身體語彙與「輪迴」的「誕生—死亡—重生」結構出發,分析《轉轉生》如何以當代目光,形塑去殖民視角的奈及利亞歷史。
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
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News