여기서 우리는 단계적 noise임을 알아야한다.
나만 그런것인지는 모르겠지만, 저런 글을 보면 한순간에 이렇게 확률로 추정 하는 것으로 착각 하는데
추론시 단계별로 stage을 주어서 점차 생성을 하는 것이다.
나는 공식을 배우다 보면
단계적 생성이 아니라 아무것도 없는 noise에서 갑자기 이미지가 생성 된다는 착각을 하게 된다.
노이지를 약간식 없애면서 생성을 하는건데
이게 사람 눈에는 약간 약간 제거 하면서 생성하고
또한 학습할때는 노이지를 약간약간 추가를 하면서 그것을 기반으로 추측을 하게 되는거다.
그러면 사람 눈에는 약간약간 추가 되는게 눈에 보이지 않지만?
컴퓨터는 이게 노이지 추가 되는것 어떻게 되는 것인지 확인이 필요하다.
사람의 눈으로 볼 때:
초기 단계에서는 노이즈가 매우 미세하게 추가되어서 거의 차이를 느끼기 어렵다. 예를 들어 t=1, t=2, t=3 시점의 이미지들은 육안으로는 거의 구분이 안 될 수 있다.
컴퓨터의 관점에서:
1. 픽셀 단위로 보면 매 단계마다 분명한 수치적 변화가 있다. 예를 들면:
- t=1: 픽셀 값 0.8 → 0.79
- t=2: 0.79 → 0.77
- t=3: 0.77 → 0.74
2. 각 단계의 노이즈는 정규분포를 따르므로 컴퓨터는:
- 각 픽셀의 값이 얼마나 변했는지
- 그 변화가 정규분포를 따르는지
- 분산이 스케줄대로 증가하는지
를 정확히 계산할 수 있다.
실제 예시를 보면:
이렇게 각 단계에서 미세한 변화들이 누적되어 결과적으로 완전한 노이즈 상태가 된다. 컴퓨터는 이 모든 미세한 변화를 정확하게 추적하고 계산할 수 있다.
PSNR (Peak Signal-to-Noise Ratio)은 이미지의 노이즈 정도를 수치적으로 평가하는 중요한 지표입니다.
이미지에 노이즈가 추가될 때:
- 시각적으로는 이미지가 얼마나 흐려지고 왜곡되는지 볼 수 있지만
- PSNR은 이 품질 저하를 정확한 수치로 보여줍니다
PSNR 값이 높을수록 원본 이미지와 비슷하다는 의미이고, 값이 낮을수록 노이즈가 많이 추가되어 품질이 저하되었다는 의미입니다.
위의 시각화 코드이다
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import matplotlib.font_manager as fm
plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
# 강아지 이미지 불러오기
image_path = "puppy.png"
original_image = np.array(Image.open(image_path)) / 255.0 # 컬러 이미지로 불러오기
# 노이즈 추가 함수
def add_noise(image, std):
noise = std * np.random.normal(size=image.shape)
noisy_image = np.clip(image + noise, 0, 1)
return noisy_image
# 8단계의 노이즈 생성 (0.01부터 0.08까지)
np.random.seed(42)
noisy_images = []
noise_levels = np.linspace(0.01, 0.08, 7) # 7개의 노이즈 레벨 생성
current_image = original_image
noisy_images.append(original_image) # 원본 이미지 추가
for noise_level in noise_levels:
current_image = add_noise(current_image, noise_level)
noisy_images.append(current_image)
# 시각화
plt.figure(figsize=(20, 4)) # 가로로 긴 형태
titles = ['Original'] + [f'Noise σ={sigma:.2f}' for sigma in noise_levels]
for i, (img, title) in enumerate(zip(noisy_images, titles)):
plt.subplot(1, 8, i+1) # 1행 8열로 배치
plt.imshow(img)
plt.title(title, fontsize=10, pad=5)
plt.axis('off')
plt.suptitle('단계별 노이즈 추가 효과', fontsize=14, y=1.05)
plt.tight_layout()
plt.show()
# PSNR 계산 및 시각화
def calculate_psnr(original, noisy):
mse = np.mean((original - noisy) ** 2)
if mse == 0:
return float('inf')
return 20 * np.log10(1.0 / np.sqrt(mse))
psnr_values = [calculate_psnr(original_image, img) for img in noisy_images]
plt.figure(figsize=(15, 4))
bars = plt.bar(titles, psnr_values,
color=['blue'] + plt.cm.autumn(np.linspace(0, 1, 7)).tolist())
plt.title('노이즈 단계별 PSNR 변화')
plt.ylabel('PSNR (dB)')
plt.xticks(rotation=45)
for bar, val in zip(bars, psnr_values):
if val != float('inf'):
plt.text(bar.get_x() + bar.get_width()/2,
val,
f'{val:.1f}dB',
ha='center',
va='bottom')
else:
plt.text(bar.get_x() + bar.get_width()/2,
plt.ylim()[1] * 0.95,
'Original',
ha='center',
va='bottom')
plt.tight_layout()
plt.show()
위에 noise를 단계적으로 추가하는 것을 보면 점차 흐릿해지는 것을 확인 할수 있다.
디퓨전 모델은 이렇게 단계적 으로 noise를 추가하고 추축하는 과정에서 학습을 하는 것이다.
여기서 학습은 노이즈 제거 과정(reverse process)은 학습이 필요하다:
이 과정에서 신경망은 각 단계에서 노이즈가 있는 이미지를 보고 원본 이미지의 방향으로 한 걸음씩 되돌리는 방법을 학습해야 한다. 구체적으로는 현재 이미지에 얼마만큼의 노이즈가 있는지를 예측하는 것을 학습한다.
이렇게 학습을 하다가 갑자기 오해가 생겼다.
결론적으로 말하면 가우시안 필터와 가우시안 노이즈를 추가하는 2가지 차이점이 헷갈렸다.
예전에 배운 것이 제대로 안 배워서 오해가 생긴 것이다.
이미지 관련 수업을 들었을때 가우시안 필터는 종모양의 가우시안 관련 있었는데
이것을 적용하면 전체적으로 종모양의 노이즈가 생기는 것이 아닌가 생각을 했다
근데 그것이 아니다.
- 가우시안 노이즈란:
- 정규분포(가우시안 분포)를 따르는 무작위 값을 이미지의 각 픽셀에 더했다
- 평균이 0이고 표준편차(σ)를 조절하여 노이즈의 강도를 제어했다
- 이미지의 모든 픽셀에 독립적으로 적용했다
- 가우시안 필터란:
- 2차원 가우시안 함수를 커널로 사용한 필터였다
- 중심 픽셀에 높은 가중치를 주고, 멀어질수록 가중치가 감소하는 패턴을 가졌다
- 이 커널을 이미지 전체에 컨볼루션하여 블러 효과를 만들었다
차이점은
- 목적이 완전히 달랐다:
- 가우시안 노이즈는 이미지에 무작위 노이즈를 추가하여 이미지를 의도적으로 손상시켰다
- 가우시안 필터는 이미지를 부드럽게 만들어 노이즈를 제거하거나 블러 효과를 주었다
- 적용 방식도 달랐다:
- 가우시안 노이즈는 각 픽셀에 무작위로 값을 더하는 방식이었다
- 가우시안 필터는 주변 픽셀들의 가중 평균을 계산하는 컨볼루션 연산을 수행했다
- 결과도 정반대였다:
- 가우시안 노이즈는 이미지를 거칠게 만들었다
- 가우시안 필터는 이미지를 부드럽게 만들었다
- 사용 목적도 달랐다:
- 가우시안 노이즈는 데이터 증강이나 모델의 강건성 테스트에 사용했다
- 가우시안 필터는 이미지 전처리나 edge detection 전 단계에 사용했다
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# 한글 폰트 설정
plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False
# 2D 그리드 생성
x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
# 1. 가우시안 필터 커널 생성 (2D 가우시안 함수)
sigma = 1.0
Z_filter = (1/(2*np.pi*sigma**2)) * np.exp(-(X**2 + Y**2)/(2*sigma**2))
# 2. 가우시안 노이즈 생성 (2D 평면에 랜덤 노이즈)
np.random.seed(42)
Z_noise = np.random.normal(0, sigma, X.shape)
# 시각화
fig = plt.figure(figsize=(15, 5))
# 1. 가우시안 필터 커널 3D 시각화
ax1 = fig.add_subplot(121, projection='3d')
surf1 = ax1.plot_surface(X, Y, Z_filter, cmap='viridis')
ax1.set_title('가우시안 필터 커널\n(2D 가우시안 함수)', pad=10)
ax1.set_xlabel('X')
ax1.set_ylabel('Y')
ax1.set_zlabel('가중치')
fig.colorbar(surf1, ax=ax1, shrink=0.5, aspect=5)
# 2. 가우시안 노이즈 3D 시각화
ax2 = fig.add_subplot(122, projection='3d')
surf2 = ax2.plot_surface(X, Y, Z_noise, cmap='viridis')
ax2.set_title('가우시안 노이즈\n(2D 랜덤 노이즈)', pad=10)
ax2.set_xlabel('X')
ax2.set_ylabel('Y')
ax2.set_zlabel('노이즈 값')
fig.colorbar(surf2, ax=ax2, shrink=0.5, aspect=5)
plt.tight_layout()
plt.show()
# 2D 히트맵으로도 시각화
plt.figure(figsize=(12, 4))
# 1. 가우시안 필터 커널 2D 히트맵
plt.subplot(121)
plt.imshow(Z_filter, cmap='viridis')
plt.colorbar()
plt.title('가우시안 필터 커널\n(2D 히트맵)')
# 2. 가우시안 노이즈 2D 히트맵
plt.subplot(122)
plt.imshow(Z_noise, cmap='viridis')
plt.colorbar()
plt.title('가우시안 노이즈\n(2D 히트맵)')
plt.tight_layout()
plt.show()
이렇게 쓰임새가 다르다
실제 강아지 사진을 가지고 적용
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
from scipy.ndimage import gaussian_filter
# 한글 폰트 설정
plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False
# 이미지 불러오기
image_path = "puppy.png"
original_image = np.array(Image.open(image_path)) / 255.0
# 가우시안 노이즈 추가 함수
def add_gaussian_noise(image, std):
noise = std * np.random.normal(size=image.shape)
noisy_image = np.clip(image + noise, 0, 1)
return noisy_image
# 여러 단계의 가우시안 노이즈와 가우시안 필터 적용
noise_levels = [0.02, 0.05, 0.08]
filter_sigmas = [1, 2, 3]
# 결과 이미지 저장
noisy_images = [add_gaussian_noise(original_image, std) for std in noise_levels]
filtered_images = [gaussian_filter(original_image, sigma) for sigma in filter_sigmas]
# 시각화
plt.figure(figsize=(15, 8))
# 원본 이미지
plt.subplot(2, 4, 1)
plt.imshow(original_image)
plt.title('원본 이미지', fontsize=12)
plt.axis('off')
# 가우시안 노이즈 결과
for i, (img, std) in enumerate(zip(noisy_images, noise_levels)):
plt.subplot(2, 4, i+2)
plt.imshow(img)
plt.title(f'가우시안 노이즈\n(σ={std})', fontsize=12)
plt.axis('off')
# 가우시안 필터 결과
for i, (img, sigma) in enumerate(zip(filtered_images, filter_sigmas)):
plt.subplot(2, 4, i+6)
plt.imshow(img)
plt.title(f'가우시안 필터\n(σ={sigma})', fontsize=12)
plt.axis('off')
plt.suptitle('가우시안 노이즈 vs 가우시안 필터 비교', fontsize=14, y=0.95)
plt.tight_layout()
plt.show()
# 특정 영역 확대해서 보기 (선택적)
def show_detail(images, titles, region=(100,100,200,200)):
plt.figure(figsize=(15, 4))
for i, (img, title) in enumerate(zip(images, titles)):
plt.subplot(1, len(images), i+1)
plt.imshow(img[region[0]:region[2], region[1]:region[3]])
plt.title(title, fontsize=12)
plt.axis('off')
plt.suptitle('상세 영역 비교', fontsize=14, y=1.05)
plt.tight_layout()
plt.show()
# 상세 비교를 위한 이미지들
detail_images = [original_image, noisy_images[1], filtered_images[1]]
detail_titles = ['원본', f'노이즈 (σ={noise_levels[1]})', f'필터 (σ={filter_sigmas[1]})']
# 상세 영역 시각화
show_detail(detail_images, detail_titles)
참고글
https://lilianweng.github.io/posts/2021-07-11-diffusion-models/
https://encord.com/blog/diffusion-models/
https://process-mining.tistory.com/182
'인공지능 공부 > 모델' 카테고리의 다른 글
디퓨전 모델 공부해보자2 베이즈 정리는 디퓨전 모델의 핵심 인가? (1) | 2024.12.22 |
---|---|
디퓨전 모델 공부해보자1 모델이 어떻게 작동할까? (0) | 2024.12.22 |