기계학습 특강 (7주차) 10월19일
딥러닝의 기초 - 드랍아웃, 이미지자료분석
- imports
- 깊은신경망-- 오버피팅
- 깊은신경망-- 드랍아웃
- 이미지자료분석-- data
- 이미지자료분석-- CNN 예비학습
- 이미지자료분석-- CNN 구현 (CPU)
- 이미지자료분석-- CNN 구현 (GPU)
import torch
from fastai.vision.all import *
import matplotlib.pyplot as plt
import torchvision
import graphviz
def gv(s): return graphviz.Source('digraph G{ rankdir="LR"'+s + '; }');
-
model: $y_i = (0\times x_i) + \epsilon_i$
torch.manual_seed(5)
x=torch.linspace(0,1,100).reshape(100,1)
y=torch.randn(100).reshape(100,1)*0.01
plt.plot(x,y)
torch.manual_seed(1)
net = torch.nn.Sequential(
torch.nn.Linear(in_features=1,out_features=512),
torch.nn.ReLU(),
torch.nn.Linear(in_features=512,out_features=1)
)
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.Adam(net.parameters())
for epoc in range(1000):
## 1
yhat = net(x)
## 2
loss = loss_fn(yhat,y)
## 3
loss.backward()
## 4
optimizr.step()
optimizr.zero_grad()
plt.plot(x,y)
plt.plot(x,net(x).data, '--')
-
데이터를 8:2로 나눈다
xtr = x[:80]
ytr = y[:80]
xtest = x[80:]
ytest = y[80:]
x.shape, xtr.shape, xtest.shape
y.shape, ytr.shape, ytest.shape
plt.plot(xtr,ytr,'o')
plt.plot(xtest,ytest,'o')
-
(xtr,ytr) 만 가지고 net를 학습시킨다.
torch.manual_seed(1)
net = torch.nn.Sequential(
torch.nn.Linear(in_features=1,out_features=512),
torch.nn.ReLU(),
torch.nn.Linear(in_features=512,out_features=1)
)
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.Adam(net.parameters())
for epoc in range(1000):
## 1
# yhat
## 2
loss = loss_fn(net(xtr),ytr)
## 3
loss.backward()
## 4
optimizr.step()
optimizr.zero_grad()
plt.plot(xtr,ytr,'o')
plt.plot(xtest,ytest,'o')
plt.plot(x,net(x).data,'--k')
#plt.plot(xtr,net(xtr).data,'--k')
#plt.plot(xtest,net(xtest).data,'--k')
(서연 필기) 오차항이 너무 잘 따라가면 영향을 미칠 수 있다.
데이터에 비해 노드 수가 많으면 오버피팅의 가능성
- 한 변수로 모든 변수 맞추는 우연을 마주한다면?
- 모델에 비해 feature가 너무 클때?
- 위를 예로 들면 input은 1이었는데 output은 512렸다
차원의 저주
-
오버피팅의 해결책: 드랍아웃
동등한 초기값에서 시작한다고 설명
- manual_seed 정해준거
torch.manual_seed(1)
net = torch.nn.Sequential(
torch.nn.Linear(in_features=1,out_features=512),
torch.nn.ReLU(),
torch.nn.Dropout(0.8),
torch.nn.Linear(in_features=512,out_features=1)
)
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.Adam(net.parameters())
for epoc in range(1000):
## 1
#
## 2
loss = loss_fn(net(xtr),ytr)
## 3
loss.backward()
## 4
optimizr.step()
optimizr.zero_grad()
계속 바뀌는 plot
plt.plot(xtr,ytr,'o')
plt.plot(xtest,ytest,'o')
plt.plot(x,net(x).data,'--k')
plt.title(r"network is in training mode",fontsize=15)
-
올바른 사용법
net.training
evaliation method 사용
net.eval()
net.training
plt.plot(xtr,ytr,'o')
plt.plot(xtest,ytest,'o')
plt.plot(x,net(x).data,'--k')
plt.title(r"network is in evaluation mode",fontsize=15)
_x = torch.linspace(0,1,101)
_x
dout = torch.nn.Dropout(0.9)
dout(_x)
- 90%의 드랍아웃: 드랍아웃층의 입력 중 임의로 90%를 골라서 결과를 0으로 만든다. + 그리고 0이 되지않고 살아남은 값들은 10배 만큼 값이 커진다.
-
드랍아웃레이어 정리
- 구조: 입력 -> 드랍아웃레이어 -> 출력
- 역할: (1) 입력의 일부를 임의로 0으로 만드는 역할 (2) 0이 안된것들은 스칼라배하여 드랍아웃을 통과한 모든 숫자들의 총합이 일정하게 되도록 조정
- 효과: 오버피팅을 억제하는 효과가 있음 (왜??)
- 추측일뿐!
- 의미: each iteration (each epoch x) 마다 학습에 참여하는 노드가 로테이션으로 랜덤으로 결정됨.
- 느낌: 모든 노드가 골고루 학습가능 + 한 두개의 특화된 능력치가 개발되기 보다 평균적인 능력치가 전반적으로 개선됨
(서연 필기) 지배적인 예측 값들보다 비지배적인 예측값을 건들려고 하면 의미가 없음.
-
download data
path = untar_data(URLs.MNIST)
-
training set
X0 = torch.stack([torchvision.io.read_image(str(fname)) for fname in (path/'training/0').ls()])
X1 = torch.stack([torchvision.io.read_image(str(fname)) for fname in (path/'training/1').ls()])
X = torch.concat([X0,X1])/255
y = torch.tensor([0.0]*len(X0) + [1.0]*len(X1)).reshape(-1,1)
-
test set
X0 = torch.stack([torchvision.io.read_image(str(fname)) for fname in (path/'testing/0').ls()])
X1 = torch.stack([torchvision.io.read_image(str(fname)) for fname in (path/'testing/1').ls()])
XX = torch.concat([X0,X1])/255
yy = torch.tensor([0.0]*len(X0) + [1.0]*len(X1)).reshape(-1,1)
X.shape,XX.shape,y.shape,yy.shape
-
교재의 모형
gv('''
splines=line
subgraph cluster_1{
style=filled;
color=lightgrey;
"x1"
"x2"
".."
"x784"
label = "Layer 0"
}
subgraph cluster_2{
style=filled;
color=lightgrey;
"x1" -> "node1"
"x2" -> "node1"
".." -> "node1"
"x784" -> "node1"
"x1" -> "node2"
"x2" -> "node2"
".." -> "node2"
"x784" -> "node2"
"x1" -> "..."
"x2" -> "..."
".." -> "..."
"x784" -> "..."
"x1" -> "node30"
"x2" -> "node30"
".." -> "node30"
"x784" -> "node30"
label = "Layer 1: ReLU"
}
subgraph cluster_3{
style=filled;
color=lightgrey;
"node1" -> "y"
"node2" -> "y"
"..." -> "y"
"node30" -> "y"
label = "Layer 2: Sigmoid"
}
''')
-
왜 28$\times$28 이미지를 784개의 벡터로 만든 다음에 모형을 돌려야 하는가?
-
기존에 개발된 모형이 회귀분석 기반으로 되어있어서 결국 회귀분석 틀에 짜 맞추어서 이미지자료를 분석하는 느낌
-
observation의 차원은 $784$가 아니라 $1\times (28\times 28)$이 되어야 맞다.
-
예전
$\underset{(n,784)}{\bf X} \overset{l_1}{\to} \underset{(n,30)}{\boldsymbol u^{(1)}} \overset{relu}{\to} \underset{(n,30)}{\boldsymbol v^{(1)}} \overset{l_2}{\to} \underset{(n,1)}{\boldsymbol u^{(2)}} \overset{sig}{\to} \underset{(n,1)}{\boldsymbol v^{(2)}}=\underset{(n,1)}{\hat{\boldsymbol y}}$
- $l_1$: 선형변환, feature를 뻥튀기하는 역할
- $\sim$ 꺾인 선이 많아진다
- $relu$: 뻥튀기된 feature에 비선형을 추가하여 표현력 극대화
- $l_2$: 선형변환, 뻥튀기된 feature를 요약 하는 역할 (=데이터를 요약하는 역할)
-
새로운 아키텍처
- $conv$: feature를 뻥튀기하는 역할 (2d ver $l_1$ 느낌)
- $relu$:
- $pooling$: 데이터를 요약하는 역할
-
우선 연산하는 방법만 살펴보자.
(예시1)
torch.manual_seed(43052)
_conv = torch.nn.Conv2d(1,1,(2,2)) # 입력1, 출력1, (2,2) window size
_conv.weight.data, _conv.bias.data
_X = torch.arange(4).reshape(1,1,2,2).float()
_X
(-0.1733)*0 + (-0.4235)*1 +\
(0.1802)*2 + (0.4668)*3 + 0.2037
_conv(_X)
torch.__version__
(예시2) 잘하면 평균도 계산하겠다?
_conv.weight.data = torch.tensor([[[[1/4, 1/4],[1/4,1/4]]]])
_conv.bias.data = torch.tensor([0.0])
_conv.weight.data,_conv.bias.data
_conv(_X) , (0+1+2+3)/4
(예시3) 이동평균?
_X = torch.arange(0,25).float().reshape(1,1,5,5)
_X
_conv(_X)
(예시4) window size가 증가한다면? (2d의 이동평균느낌)
_conv = torch.nn.Conv2d(1,1,(3,3)) # 입력1, 출력1, (3,3) window size
_conv.bias.data = torch.tensor([0.0])
_conv.weight.data = torch.tensor([[[[1/9,1/9,1/9],[1/9,1/9,1/9],[1/9,1/9,1/9]]]])
(3,3)이나~ 3이나~
_X,_conv(_X)
(1+2+3+6+7+8+11+12+13)/9
(예시5) 피처뻥튀기
_X = torch.tensor([1.0,1.0,1.0,1.0]).reshape(1,1,2,2)
_X
_conv = torch.nn.Conv2d(1,8,(2,2))
_conv.weight.data.shape,_conv.bias.data.shape
_conv(_X).shape
_conv(_X).reshape(-1)
torch.sum(_conv.weight.data[0,...])+_conv.bias.data[0],\
torch.sum(_conv.weight.data[1,...])+_conv.bias.data[1]
결국 아래를 계산한다는 의미
torch.sum(_conv.weight.data,axis=(2,3)).reshape(-1)+ _conv.bias.data
_conv(_X).reshape(-1)
(잔소리) axis 사용 익숙하지 않으면 아래 꼭 들으세요..
- https://guebin.github.io/IP2022/2022/04/11/(6주차)-4월11일.html , numpy공부 4단계: 축
_X = torch.randn(25).reshape(1,5,5)
_X
a1=torch.nn.ReLU()
a1(_X)
_maxpooling = torch.nn.MaxPool2d((2,2))
_X = torch.arange(16).float().reshape(1,4,4)
_X, _maxpooling(_X)
가장 중요한 특징만 남게 될 것이다.
_X = torch.arange(25).float().reshape(1,5,5)
_X, _maxpooling(_X)
버려지는 데이터
_X = torch.arange(36).float().reshape(1,6,6)
_X, _maxpooling(_X)
X.shape
c1 = torch.nn.Conv2d(1,16,(5,5))
print(X.shape)
print(c1(X).shape)
a1 = torch.nn.ReLU()
print(X.shape)
print(c1(X).shape)
print(a1(c1(X)).shape)
m1 = torch.nn.MaxPool2d((2,2))
print(X.shape)
print(c1(X).shape)
print(a1(c1(X)).shape)
print(m1(a1(c1(X))).shape)
-
펼치자.
(방법1)
m1(a1(c1(X))).reshape(-1,2304).shape
16*12*12
(방법2)
flttn = torch.nn.Flatten()
print(X.shape)
print(c1(X).shape)
print(a1(c1(X)).shape)
print(m1(a1(c1(X))).shape)
print(flttn(m1(a1(c1(X)))).shape)
-
2304 $\to$ 1 로 차원축소하는 선형레이어를 설계
l1 = torch.nn.Linear(in_features=2304,out_features=1)
print(X.shape)
print(c1(X).shape)
print(a1(c1(X)).shape)
print(m1(a1(c1(X))).shape)
print(flttn(m1(a1(c1(X)))).shape)
print(l1(flttn(m1(a1(c1(X))))).shape)
-
시그모이드
a2 = torch.nn.Sigmoid()
l1 = torch.nn.Linear(in_features=2304,out_features=1)
print(X.shape)
print(c1(X).shape)
print(a1(c1(X)).shape)
print(m1(a1(c1(X))).shape)
print(flttn(m1(a1(c1(X)))).shape)
print(l1(flttn(m1(a1(c1(X))))).shape)
print(a1(l1(flttn(m1(a1(c1(X)))))).shape)
-
네트워크 설계
net = torch.nn.Sequential(
c1, # 2d: 컨볼루션(선형변환), 피처 뻥튀기
a1, # 2d: 렐루(비선형변환)
m1, # 2d: 맥스풀링: 데이터요약
flttn, # 2d->1d
l1, # 1d: 선형변환
a2 # 1d: 시그모이드(비선형변환)
)
loss_fn = torch.nn.BCELoss()
optimizr = torch.optim.Adam(net.parameters())
t1= time.time()
for epoc in range(100):
## 1
yhat = net(X)
## 2
loss = loss_fn(yhat,y)
## 3
loss.backward()
## 4
optimizr.step()
optimizr.zero_grad()
t2= time.time()
t2-t1
plt.plot(y)
plt.plot(net(X).data,'.')
plt.title('Traning Set',size=15)
plt.plot(yy)
plt.plot(net(XX).data,'.')
plt.title('Test Set',size=15)
ds1=torch.utils.data.TensorDataset(X,y)
ds2=torch.utils.data.TensorDataset(XX,yy)
X.shape
len(X)/10
len(XX)
dl1 = torch.utils.data.DataLoader(ds1,batch_size=1266)
dl2 = torch.utils.data.DataLoader(ds2,batch_size=2115)
dls = DataLoaders(dl1,dl2) # 이거 fastai 지원함수입니다
net = torch.nn.Sequential(
torch.nn.Conv2d(1,16,(5,5)),
torch.nn.ReLU(),
torch.nn.MaxPool2d((2,2)),
torch.nn.Flatten(),
torch.nn.Linear(2304,1),
torch.nn.Sigmoid()
)
loss_fn = torch.nn.BCELoss()
lrnr = Learner(dls,net,loss_fn)
lrnr.fit(10)
lrnr.model
net.to("cpu")
-
결과를 시각화하면 아래와 같다.
plt.plot(net(X).data,'.')
plt.title("Training Set",size=15)
plt.plot(net(XX).data,'.')
plt.title("Test Set",size=15)
1/10만 사용했는데 잘 training된 것 같다
-
빠르고 적합결과도 좋음
lrnr.model
net
id(lrnr.model), id(net)
lrnr.model(X)
net(X)
같은 결과
20221026 수업
-
BCEWithLogitsLoss = Sigmoid + BCELoss
- 왜 써요? 수치적으로 더 안정
torch.nn.BCEWithLogitsLoss
- This loss combines a
Sigmoid
layer and theBCELoss
in one single class. This version is more numerically stable than using a plainSigmoid
followed by aBCELoss
as, by combining the operations into one layer, we take advantage of the log-sum-exp trick for numerical stability.
-
사용방법
(1) dls 만들기
ds1=torch.utils.data.TensorDataset(X,y)
ds2=torch.utils.data.TensorDataset(XX,yy)
torch.utils.data.TensorDataset?
dl1 = torch.utils.data.DataLoader(ds1,batch_size=1266)
dl2 = torch.utils.data.DataLoader(ds2,batch_size=2115)
dls = DataLoaders(dl1,dl2) # 이거 fastai 지원함수입니다
(2) lrnr생성
net = torch.nn.Sequential(
torch.nn.Conv2d(1,16,(5,5)),
torch.nn.ReLU(),
torch.nn.MaxPool2d((2,2)),
torch.nn.Flatten(),
torch.nn.Linear(2304,1),
#torch.nn.Sigmoid()
)
loss_fn = torch.nn.BCEWithLogitsLoss()
lrnr = Learner(dls,net,loss_fn)
(3) 학습
lrnr.fit(10)
(4) 예측 및 시각화
net.to("cpu")
시각화 위해서 cpu로 옮겨주기
net(X)
sigmoid 취하기 전이지 우리는 bcewithlogiticsLoss 썼잖아, 그래서 0~1사이 아님
a2(torch.tensor(0))
fig,ax = plt.subplots(1,2,figsize=(8,4))
ax[0].plot(net(X).data,',',color="C1")
ax[1].plot(y)
ax[1].plot(a2(net(X)).data,',')
fig.suptitle("Training Set",size=15)
fig,ax = plt.subplots(1,2,figsize=(8,4))
ax[0].plot(net(XX).data,',',color="C1")
ax[1].plot(yy)
ax[1].plot(a2(net(XX)).data,',')
fig.suptitle("Test Set",size=15)