-
Notifications
You must be signed in to change notification settings - Fork 4
오그멘테이션 하이퍼파라미터 수정 #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
a583155
fcb8508
3364dc6
dc55d07
ea7a5e8
8ecd211
574e269
943e85f
28a7fc1
f2d8c74
6de5887
b3e85a5
6433bd1
afc72c9
5ce21d4
00f6e5e
933f67c
702eaa8
693c81d
4539c07
c9a39af
0c22a0f
f119606
3f24d24
e60db94
6832bd9
83b87c9
0ebaacd
fb7e9ac
b7df5c0
53433a5
46330c8
4e0eed0
d4fa978
3a24004
4b50002
0b809bf
142794d
77b6743
875ec6c
d375683
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| import json | ||
| import cv2 | ||
| from matplotlib import pyplot as plt | ||
| from albumentations import ( | ||
| HorizontalFlip, IAAPerspective, ShiftScaleRotate, CLAHE, RandomRotate90, | ||
| Transpose, ShiftScaleRotate, Blur, OpticalDistortion, GridDistortion, HueSaturationValue, | ||
| IAAAdditiveGaussianNoise, GaussNoise, MotionBlur, MedianBlur, IAAPiecewiseAffine, | ||
| IAASharpen, IAAEmboss, RandomBrightnessContrast, Flip, OneOf, Compose | ||
| ) | ||
|
|
||
|
|
||
| def aug(config:{}, p=0.5): | ||
| return Compose([ | ||
| HorizontalFlip(True), | ||
| RandomRotate90(True), | ||
| Flip(), | ||
| Transpose(), | ||
| OneOf([ | ||
| IAAAdditiveGaussianNoise(), | ||
| GaussNoise(), | ||
| ], p=0.2), | ||
| OneOf([ | ||
|
|
||
| MotionBlur(p=config["MotionBlur"]), | ||
| MedianBlur(blur_limit=config["blur_limit"], p=0.1), | ||
| Blur(blur_limit=config["blur_limit"], p=0.1), | ||
| ], p=0.2), | ||
| ShiftScaleRotate(shift_limit=config["shift_limit"], scale_limit=config['scale_limit'], | ||
| rotate_limit=config["rotate_limit"], p=0.2), | ||
| OneOf([ | ||
| OpticalDistortion(p=0.3), | ||
| GridDistortion(p=0.1), | ||
| IAAPiecewiseAffine(p=0.3), | ||
| ], p=0.2), | ||
| OneOf([ | ||
| CLAHE(clip_limit=config["clip_limit"]), | ||
| IAASharpen(), | ||
| IAAEmboss(), | ||
| RandomBrightnessContrast(), | ||
|
|
||
| ], p=0.3), | ||
| HueSaturationValue(p=config["HueSaturationValue"]), | ||
| ], p=p) | ||
|
|
||
|
|
||
| def show_img(img, figsize=(8, 8)): | ||
| fig, ax = plt.subplots(figsize=figsize) | ||
| ax.grid(False) | ||
| ax.set_yticklabels([]) | ||
| ax.set_xticklabels([]) | ||
| ax.imshow(img) | ||
| plt.imshow(img) | ||
|
|
||
|
|
||
| # with open('aug_config.json') as json_file: | ||
| # json_data = json.load(json_file) | ||
| # print(json_data['MotionBlur']) | ||
|
|
||
| if __name__ == "__main__": | ||
| image = cv2.imread('dog.12473.jpg') | ||
| config = { | ||
| "MotionBlur": 0.2, | ||
| "blur_limit": 3, | ||
| "MedianBlur": 0.1, | ||
| "shift_limit": 0.0625, | ||
| "scale_limit": 0.2, | ||
| "rotate_limit": 45, | ||
| "clip_limit": 2, | ||
| "HueSaturationValue": 0.3 | ||
| } | ||
| augmentation = aug(config) | ||
| data = {'image': image} | ||
| augmented = augmentation(**data) | ||
| image = augmented['image'] | ||
| show_img(image) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,7 @@ | |
| import torch | ||
| import torch.nn as nn | ||
| import torchvision.transforms as transforms | ||
|
|
||
| from augmentations import aug | ||
| from torchsummary import summary | ||
| from torch.nn import functional as F | ||
| from torch.utils.data import DataLoader | ||
|
|
@@ -77,15 +77,15 @@ def configure_optimizers(self): | |
|
|
||
| @pl.data_loader | ||
| def train_dataloader(self): | ||
| return DataLoader(MNIST(os.getcwd(), train=True, download=True, transform=transforms.ToTensor()), batch_size=128) | ||
| return DataLoader(MNIST(os.getcwd(), train=True, download=True, transform=aug()), batch_size=128) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기서
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 모델에서는 테스트를 못했더니 미스가 났군요ㅜ 수정하였습니다.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 항상 짜신 모듈에 대해서는 철저하게 테스트 하셔야합니다. |
||
|
|
||
| @pl.data_loader | ||
| def val_dataloader(self): | ||
| return DataLoader(MNIST(os.getcwd(), train=True, download=True, transform=transforms.ToTensor()), batch_size=32) | ||
| return DataLoader(MNIST(os.getcwd(), train=True, download=True, transform=aug()), batch_size=32) | ||
|
|
||
| @pl.data_loader | ||
| def test_dataloader(self): | ||
| return DataLoader(MNIST(os.getcwd(), train=False, download=True, transform=transforms.ToTensor()), batch_size=32) | ||
| return DataLoader(MNIST(os.getcwd(), train=False, download=True, transform=aug()), batch_size=32) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| import os | ||
| import torch | ||
| import torch.nn as nn | ||
| import pytorch_lightning as pl | ||
| import torch.nn.functional as F | ||
| import torchvision.transforms as transforms | ||
| from pytorch_lightning import Trainer | ||
| from augmentations import aug | ||
| from torchvision.models import resnet50 | ||
| from torch.utils.data import DataLoader | ||
| from torchvision.datasets import CIFAR10 | ||
|
|
||
|
|
||
| def conv3x3(in_channels, out_channels, stride=1): | ||
| return nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False) | ||
|
|
||
|
|
||
| class ResidualBlock(nn.Module): | ||
| def __init__(self, in_channels, out_channels, stride=1, downsample=None): | ||
| super(ResidualBlock, self).__init__() | ||
| self.conv1 = conv3x3(in_channels, out_channels, stride) | ||
| self.bn1 = nn.BatchNorm2d(out_channels) | ||
| self.relu = nn.ReLU(inplace=True) | ||
| self.conv2 = conv3x3(out_channels, out_channels) | ||
| self.bn2 = nn.BatchNorm2d(out_channels) | ||
| self.downsample = downsample | ||
|
|
||
| def forward(self, x): | ||
| residual = x | ||
| out = self.conv1(x) | ||
| out = self.bn1(out) | ||
| out = self.conv2(out) | ||
| out = self.bn2(out) | ||
| if self.downsample: | ||
| residual = self.downsample(x) | ||
| out += residual | ||
| out = self.relu(out) | ||
| return out | ||
|
|
||
|
|
||
| class ResNet(pl.LightningModule): | ||
| def __init__(self, block, layers, num_classes): | ||
| super(ResNet, self).__init__() | ||
| self.in_channels = 16 | ||
| self.conv = conv3x3(3, 16) | ||
| self.bn = nn.BatchNorm2d(16) | ||
| self.relu = nn.ReLU(inplace=True) | ||
| self.layer1 = self.make_layer(block, 16, layers[0]) | ||
| self.layer2 = self.make_layer(block, 32, layers[1], 2) | ||
| self.layer3 = self.make_layer(block, 64, layers[2], 2) | ||
| self.avg_pool = nn.AvgPool2d(8) | ||
| self.fc = nn.Linear(64, num_classes) | ||
|
|
||
| def make_layer(self, block, out_channels, blocks, stride=1): | ||
| downsample = None | ||
| if (stride != 1) or (self.in_channels != out_channels): | ||
| downsample = nn.Sequential( | ||
| conv3x3(self.in_channels, out_channels, stride=stride), | ||
| nn.BatchNorm2d(out_channels)) | ||
| layers = [] | ||
| layers.append(block(self.in_channels, out_channels, stride, downsample)) | ||
| self.in_channels = out_channels | ||
| for i in range(1, blocks): | ||
| layers.append(block(out_channels, out_channels)) | ||
| return nn.Sequential(*layers) | ||
|
|
||
| def forward(self, x): | ||
| out = self.conv(x) | ||
| out = self.bn(out) | ||
| out = self.relu(out) | ||
| out = self.layer1(out) | ||
| out = self.layer2(out) | ||
| out = self.layer3(out) | ||
| out = self.avg_pool(out) | ||
| out = out.view(out.size(0), -1) | ||
| out = self.fc(out) | ||
| return out | ||
|
|
||
| def transform(self): | ||
| config = { | ||
| "MotionBlur": 0.2, | ||
| "blur_limit": 3, | ||
| "MedianBlur": 0.1, | ||
| "shift_limit": 0.0625, | ||
| "scale_limit": 0.2, | ||
| "rotate_limit": 45, | ||
| "clip_limit": 2, | ||
| "HueSaturationValue": 0.3 | ||
| } | ||
| augs = aug(config) | ||
| return augs | ||
|
|
||
| def loss(self, y_hat, y): | ||
| return F.cross_entropy(y_hat, y) | ||
|
|
||
| def training_step(self, batch, batch_nb): | ||
| x, y = batch | ||
| x = x.to(device) | ||
| y = y.to(device) | ||
| y_hat = self.forward(x) | ||
| return {'loss': F.cross_entropy(y_hat, y)} | ||
|
|
||
| def validation_step(self, batch, batch_nb): | ||
| x, y = batch | ||
| x = x.to(device) | ||
| y = y.to(device) | ||
| y_hat = self.forward(x) | ||
| return {'val_loss': F.cross_entropy(y_hat, y)} | ||
|
|
||
| def validation_end(self, outputs): | ||
| avg_loss = torch.stack([x['val_loss'] for x in outputs]).mean() | ||
| return {'avg_val_loss': avg_loss} | ||
|
|
||
| def test_step(self, batch, batch_nb): | ||
| x, y = batch | ||
| y_hat = self.forward(x) | ||
| return {'test_loss': F.cross_entropy(y_hat, y)} | ||
|
|
||
| def test_end(self, outputs): | ||
| avg_loss = torch.stack([x['test_loss'] for x in outputs]).mean() | ||
| return {'avg_test_loss': avg_loss} | ||
|
|
||
| def configure_optimizers(self): | ||
| return torch.optim.Adam(self.parameters(), lr=0.02) | ||
|
|
||
| @pl.data_loader | ||
| def train_dataloader(self): | ||
| return DataLoader(CIFAR10(os.getcwd(), train=True, download=True, transform=transforms.ToTensor()), batch_size=128) | ||
|
|
||
| @pl.data_loader | ||
| def val_dataloader(self): | ||
| return DataLoader(CIFAR10(os.getcwd(), train=True, download=True, transform=transforms.ToTensor()), batch_size=32) | ||
|
|
||
| @pl.data_loader | ||
| def test_dataloader(self): | ||
| return DataLoader(CIFAR10(os.getcwd(), train=False, download=True, transform=transforms.ToTensor()), | ||
| batch_size=32) | ||
|
|
||
|
|
||
| device = torch.device("cuda:0" if torch.cuda.is_available() else 'cpu') | ||
| model = ResNet(ResidualBlock, [3, 4, 6], 10).to(device) | ||
| trainer = Trainer() | ||
| trainer.fit(model) |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p값은 별도로 제어 안해도 될까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분을 고민해봤는데 나중에 라이트닝에서 사용하는 파서로 아래 방식처럼 리스트로 옵션(확률값)을 주는게 어떨까요?
parser.opt_list('--drop_prob', default=0.2, options=[0.2, 0.5], type=float, tunable=False)
사실 저 p를 어느정도까지 세밀하게 해야하는지를 모르겠습니다ㅠ
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
네네 list로 줘도 괜찮을 것 같습니다.
ShiftScaleRotate로 예를 들어보겠습니다.configuration 파일은 json으로 구성한다고 가정합시다.
(json으로 구성해서 python에서 파싱하면 dict로 파싱되니, config 데이터 타입은 dict로 가정합니다.)
ShiftScaleRotate API를 확인해보니 아래와 같이 구성되어있음을 확인할 수 있습니다.
ShiftScaleRotate(shift_limit=0.0625, scale_limit=0.1, rotate_limit=45, interpolation=1, border_mode=4, value=None, mask_value=None, always_apply=False, p=0.5)그러면 config은 다음과 같이 작성할 수 있습니다.
dict로 파싱하면 아래와 같습니다.
config 파싱한 결과는 실제 사용할 때, 아래와 같이 사용할 수 있습니다.
이렇게 사용하게되면 모든 제어를 config.json 설정을 통해서 할 수 있게됩니다.
(제 예시는 슈도코드입니다. 실제 사용하실 때는 테스트해보시면서 작동하는 것을 확인해보면서 작성하셔야합니다.)
추가적으로 라이트닝이 Argparser와 유사한 메카니즘의 파서를 사용한다면 저는 하이드라를 사용하는게 더 좋을 것 같습니다.
Argparser은 하이드라와 비교했을 때, 별도의 저장 로직이 코드상에 없다면 상대적으로 히스토리 추적이 어렵고 오타 확률이 높고 default값을 제외한 커스텀 값은 매번 타이핑 해야해서 불편합니다.
라이트닝에 탑재되어있는 파서는 저장 로직이 심어져있을 것으로 추측되지만, Argparser의 메카니즘을 따라가면 저는 하이드라를 사용하는 것이 더 좋다는 생각입니다.