from scipy import ndimage, signal
x = tf.reshape(tf.range(9), (3,3))
f = tf.ones((2,2))
ndimage.convolve(x,f,mode='nearest') # 마지막 픽셀의 값을 복사하여 채워 넣고 연산한다
# array([[ 8, 12, 14],
# [20, 24, 26],
# [26, 30, 32]], dtype=int32)
Convolution vs Transpose convolution vs Deconvolution
1.Convolution
-이미지에서특징을추출하기위한연산방법
-보통이미지에서중요한특징을추출하고연산량을줄이기위해downsampling이된다
-데이터를상하, 좌우반전후correlation연산을하는방법
2.Convolutiontranspose
-Learnableupsampling (보간법을활용하여값을채우는것과비슷하다)
-convolution연산과전혀다르지만연산결과가같을수있다
-보간법과다르게학습할수있는파라미터가있는방법이다
3.Deconvolution
-convolution의역연산
Transpose convolution
import tensorflow as tf
import numpy as np
a = np.array([1,2,3])
b = np.array([-1,1])
signal.convolve(a,b) # convolution transpose 연산결과가 같다
# array([-1, -1, -1, 3])
convolution transpose
1 -1 -1
2 -1 => -2 + 1 => -1
3 1 -3 + 2 -1
3 3
Variational AutoEncoder
내가 아는 확률 분포(정규분포)와 찾기 힘든분포를 가능한 근사적으로 하여 확률 분포를 찾아내는 방법
ELBO term을 Φ에 대해 maximize 하면 이상적인 sampling함수를 찾는 것이다 ELBO term을 𝜃에 대해 maximize 하면 MLE 관점에서 Network의 파라미터를 찾는 것이다 출처:https://deepinsight.tistory.com/127[Steve-Lee's Deep Insight]
AutoEncoder vs Variational AutoEncoder
AutoEncoder와 Variational AutoEncoder 구조가 매우 유사한데
왜 AutoEncoder는 생성모델이 아니고 VAE는 생성 모델일까?
AutoEncoder는 사전 지식에 대한 조건이 없기 때문에 의미 있는 z vector의 latent space가 계속해서 바뀐다
즉 새로운 이미지를 생성할 때 z값이 계속해서 바뀐다
반면 VAE는 사전지식에 대한 조건을 부여 했기 때문에 z vector가 사전지식(내가 아는 분포)를 따른다
autoencoder.encoder, autoencoder.decoder # 상속을 통해서 만들면 더 유연한 모델을 만들 수 있다
# (<keras.engine.sequential.Sequential at 0x7fdfc49ca910>,
# <keras.engine.sequential.Sequential at 0x7fdfc7feec90>)
x = tf.keras.layers.Conv2D(16,3, padding='same', activation='relu')(input_)
x = tf.keras.layers.MaxPooling2D(2,2, padding='same')(x)
x = tf.keras.layers.Conv2D(8,3, padding='same', activation='relu')(x)
x = tf.keras.layers.MaxPooling2D(2,2, padding='same')(x)
x = tf.keras.layers.Conv2D(8,3, padding='same', activation='relu')(x)
x = tf.keras.layers.MaxPooling2D(2,2, padding='same')(x)
x = tf.keras.layers.Conv2D(8,3, padding='same', activation='relu')(x)
x = tf.keras.layers.UpSampling2D((2,2))(x)
x = tf.keras.layers.Conv2D(8,3, padding='same', activation='relu')(x)
x = tf.keras.layers.UpSampling2D((2,2))(x)
x = tf.keras.layers.Conv2D(16,3, activation='relu')(x)
x = tf.keras.layers.UpSampling2D((2,2))(x)
# x = tf.keras.layers.Conv2D(16,3, padding='same', activation='relu')(x)
import tensorflow as tf
import xml.etree.ElementTree as ET # xml 형식의 파일을 parsing하는 파이썬 라이브러리
import pathlib
import matplotlib.pyplot as plt
import matplotlib.patches as mpt
tree = ET.parse(src) # xml 파알 구조 불러오기 (tree 형태로)
root = tree.getroot() # 시작지점 설정 (root 찾기)
root # annotation가 최상위 root (태그)
# <Element 'annotation' at 0x16107dcc0>
for i in root.iter('object'): # 5개 object가 있다 / name / pose / truncated /diffiicult / bndbox
print(i)
# <Element 'object' at 0x163992a90>
# <Element 'object' at 0x163992db0>
# <Element 'object' at 0x163995130>
# <Element 'object' at 0x163995450>
# <Element 'object' at 0x163995770>
for i in root.iter('object'): # object에 대한 정보만 필요하기 때문에 object태그의 하위 요소만 불러온다
difficult = i.find('difficult').text
cls = i.find('name').text
def annote(img_id):
'''
20개 클래스에 해당하는 class id와 4가지 위치 정보를 반환하는 함수
'''
src = f'VOCdevkit/VOC2007/Annotations/{img_id}.xml'
tree = ET.parse(src)
root = tree.getroot()
temp = []
for i in root.iter('object'):
difficult = i.find('difficult').text
cls = i.find('name').text
if cls not in classes: # or int(difficult) == 1
continue
cls_id = classes[cls]
xmlbox = i.find('bndbox')
xmin = int(xmlbox.find('xmin').text)
ymin = int(xmlbox.find('ymin').text)
xmax = int(xmlbox.find('xmax').text)
ymax = int(xmlbox.find('ymax').text)
bb = (xmin,ymin,xmax,ymax)
temp.append((cls_id, bb))
return temp
def show(img_id):
'''
이미지에 bounding box 그리는 함수
'''
im = plt.imread('VOCdevkit/VOC2007/JPEGImages/'+str(imgs[img_id])+'.jpg')
fig, ax = plt.subplots(1,1)
for i in annote(imgs[img_id]):
rec = mpt.Rectangle((i[1][0],i[1][1]), i[1][2]-i[1][0], i[1][3]-i[1][1], edgecolor='red', fill=False)
ax.add_patch(rec)
ax.imshow(im)
# 상속하는 방식(프레임워크에 한정적이지만 tensorflow에 최적화된 기능을 사용할 수 있다) / seqence data처럼 사용할 수 있기 때문에 학습 데이터 유연하게 전달할 수 있다
tf.keras.utils.Sequence
# keras.utils.data_utils.Sequence
Numpy 형태 데이터를 학습을 하면 자동으로 tensor로 변환하여 학습이 가능하긴 하지만
이러한 tensor는 데이터 전체 메모리를 한꺼번에 올리기 때문에 컴퓨터 성능에 따라 학습이 불가능할 수도 있다
뿐만아니라 메모리를 효율적으로 사용하지 못하기 때문에 학습 속도가 비교적 느릴 수 있다
반면 tf.data.Dataset은 내부적으로 최적화 되어 있기 때문에 cpu, gpu를 동시에 사용하거나
prefetch, cache기법 등 다양한 방법으로 최적화된 학습이 가능하다
따라서 데이터를 generator형태로 불러오거나 tf.data.Dataset으로 변환하는 과정은 중요하다
(단, lazy 방식으로 만들어져 있기 때문에 데이터 하나 확인하는 것이 매우 까다롭다
Network Design
lre = tf.keras.layers.LeakyReLU(alpha=0.1) # 자주 쓰는 것들은 layers에 포함되어 있다
input_ = tf.keras.Input((448,448,3))
x = tf.keras.layers.Conv2D(64,7,2, padding='same', activation=lre)(input_)
x = tf.keras.layers.MaxPool2D(2,2)(x)
x = tf.keras.layers.Conv2D(192,3, padding='same', activation=lre)(x)
x = tf.keras.layers.MaxPool2D(2,2)(x)
x = tf.keras.layers.Conv2D(128,1, padding='same', activation=lre)(x)
x = tf.keras.layers.Conv2D(256,3, padding='same', activation=lre)(x)
x = tf.keras.layers.Conv2D(256,1, padding='same', activation=lre)(x)
x = tf.keras.layers.Conv2D(512,3, padding='same', activation=lre)(x)
x = tf.keras.layers.MaxPool2D(2,2)(x)
x = tf.keras.layers.Conv2D(256,1, padding='same', activation=lre)(x)
x = tf.keras.layers.Conv2D(512,3, padding='same', activation=lre)(x)
x = tf.keras.layers.Conv2D(256,1, padding='same', activation=lre)(x)
x = tf.keras.layers.Conv2D(512,3, padding='same', activation=lre)(x)
x = tf.keras.layers.Conv2D(256,1, padding='same', activation=lre)(x)
x = tf.keras.layers.Conv2D(512,3, padding='same', activation=lre)(x)
x = tf.keras.layers.Conv2D(256,1, padding='same', activation=lre)(x)
x = tf.keras.layers.Conv2D(512,3, padding='same', activation=lre)(x)
x = tf.keras.layers.Conv2D(512,1, padding='same', activation=lre)(x)
x = tf.keras.layers.Conv2D(1024,3, padding='same', activation=lre)(x)
x = tf.keras.layers.MaxPool2D(2,2)(x)
x = tf.keras.layers.Conv2D(512,1, padding='same', activation=lre)(x)
x = tf.keras.layers.Conv2D(1024,3, padding='same', activation=lre)(x)
x = tf.keras.layers.Conv2D(512,1, padding='same', activation=lre)(x)
x = tf.keras.layers.Conv2D(1024,3, padding='same', activation=lre)(x)
x = tf.keras.layers.Conv2D(1024,3,2, padding='same', activation=lre)(x)
x = tf.keras.layers.Conv2D(1024,3, padding='same', activation=lre)(x)
x = tf.keras.layers.Conv2D(1024,3, padding='same', activation=tf.keras.activations.linear)(x)
x = tf.keras.layers.Flatten()(x)
x = tf.keras.layers.Dense(4096, activation=lre)(x)
x = tf.keras.layers.Dropout(0.5)(x)
x = tf.keras.layers.Dense(1470)(x)
x = tf.keras.layers.Reshape((7,7,30))(x)
10개 -> 두 개의 bounding box / 20개 -> 각각의 클래스 확률 결과를 도출하기 위해서는
Custom layers를 통해 직접 loss function을 구현해야 한다
Regression
Activation function을 linear함수(값의 범위만 정한다)를 사용하거나 사용하지 않으면 Regression 모델을 만들 수 있다
Loss function을 Crossentropy대신 MSE를 사용하면 된다
!pip install -U tensorflow-addons
# 최신 기법 + 중요성이 살짝 떨어지는 것들을 모아둔 추가 패키지 / 기본 tensorflow에 없는 layer를 사용할 수 있다
!pip install typeguard
# How to install tensorflow addons on mac with m1
!pip install --upgrade --force --no-dependencies https://github.com/apple/tensorflow_macos/releases/download/v0.1alpha3/tensorflow_addons_macos-0.1a3-cp38-cp38-macosx_11_0_arm64.whl
x = np.arange(8,800,16)
y = np.arange(8,800,16)
ratio = [0.5,1,2]
scale = [8,16,32]
al = np.zeros((22500,4))
count = 0
cl = np.array(np.meshgrid(x,y)).T.reshape(-1,2)
for i in cl:
cx, cy = i[0], i[1]
for r in ratio:
for s in scale:
h = pow(pow(s,2)/r,0.5)
w = h*r
h *= 16
w *= 16
xmin = cx-0.5*w
ymin = cy-0.5*h
xmax = cx+0.5*w
ymax = cy+0.5*h
al[count] = [xmin,ymin,xmax,ymax]
count += 1
def smooth_l1_loss(y_truee, y_pred,smooth=1):
x = tf.abs(y_true, y_pred)
mask = tf.cast(tf.less(x,1.0), tf.float32)
loss = (mask*(0.5*x**2)) + (1-mask)*(x-0.5)
return loss
class MyLoss(tf.keras.losses.Loss):
def call(self,y_true,y_pred,smooth=1):
x = tf.abs(y_true, y_pred)
mask = tf.cast(tf.less(x,1.0), tf.float32)
loss = (mask*(0.5*x**2)) + (1-mask)*(smooth-0.5)
return loss
from functools import partial
mys = partial(smooth_l1_loss, smooth=2)