├── .gitignore ├── Dockerfile ├── README.md ├── imgs └── pushed_screenshot.png ├── main.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM pytorch/pytorch:1.0-cuda10.0-cudnn7-runtime 2 | 3 | COPY . /root/example 4 | 5 | WORKDIR /root/example 6 | 7 | RUN pip install pip -U && pip install -r requirements.txt 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Basic Pytorch Docker Image Example 2 | 3 | [![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2FAppleHolic%2FPytorchDockerExample)](https://hits.seeyoufarm.com) 4 | 5 | ### 소개 6 | - Pytorch 기반의 딥러닝 학습 코드를 Docker 환경에서 실행하기 위한 간단한 예제를 준비해보았습니다. 7 | - MNIST example code : [https://github.com/pytorch/examples/blob/master/mnist](https://github.com/pytorch/examples/blob/master/mnist) 8 | 9 |
10 | 11 | ### Docker 파일 및 빌드, 실행에 대한 설명 12 | 13 |
14 | 15 | #### Dockerfile 16 | - Dockerfile은 docker 실행 환경이 설치된 ([install link](https://docs.docker.com/install/)) 컴퓨터에 실행 환경이 갖춰진 상태로 코드를 실행 할 수 있는 이미지를 빌드하는 Command들을 순차적으로 나열한 코드 파일입니다. 17 | 18 | - Line by line 설명 19 | 20 | 1. FROM : FROM은 기존에 빌드된 다른 이미지로부터 빌드를 시작하기 원할 때 사용합니다. 이번 예시에서는 pytorch 1.0 버전을 기반으로 빌드하였습니다. 최신 버전의 태그는 [pytorch dockerhub](https://hub.docker.com/r/pytorch/pytorch/tags) 에서 확인하실 수 있습니다. 21 | ``` 22 | FROM pytorch/pytorch:1.0-cuda10.0-cudnn7-runtime 23 | ``` 24 | 25 | 2. COPY : Code repository를 기준으로 가상의 Docker 이미지 상에 파일들을 복사하기 위한 지시어입니다. 이 예제에서는 main.py 및 requirements.txt를 복사하기 위해 사용하였습니다. 26 | ``` 27 | COPY . /root/example 28 | ``` 29 | 30 | 3. WORKDIR : CMD 명령어 등 어떤 명령어가 실행이 될 때 base가 되는 directory를 지정합니다. 코드를 복사해둔 디렉토리를 지정하여 docker를 실행하는 분이 main.py가 포함된 디렉토리를 몰라도 실행가능하도록 하였습니다. 31 | ``` 32 | WORKDIR /root/example 33 | ``` 34 | 35 | 4. RUN : 일반적으로 bash 에 입력하는 명령어들을 실행하는 지시어입니다. 필요한 python package가 나열되어 있는 requirements.txt를 pip 명령어로 설치 해 줍니다. 36 | ``` 37 | RUN pip install pip -U && pip install -r requirements.txt 38 | ``` 39 | 40 |
41 | 42 | #### 빌드 및 실행 43 | 44 | 1. 빌드 : 이미지를 만들기 위한 기본적인 명령어 예시. 실행 시 Dockerfile에 적힌 지시어들에 따라 순차적으로 빌드가 됩니다. pytorch image pulling에 다소 시간이 소요됩니다. 45 | - appleholic은 아래의 docker hub username 입니다. 46 | - appleholic/pytorchmnistexample : repository 이름 47 | - v0.1 : 해당 repository 에서 이미지에 대한 버전 관리를 위한 태그 입니다 48 | - \-t : tag argument 49 | 50 | ```bash 51 | $$ docker build . -t appleholic/pytorchmnistexample:v0.1 52 | ``` 53 | 54 | 2. 실행 : 빌드가 완료된 후 다음의 명령어로 실행이 가능합니다. 55 | - \-it : \-i 와 \-t 옵션이 결합된 형태이며, shell과 iteractive 하게 작업(stdin/out)을 하기 위해 같이 사용됩니다. (\-i STDIN 관련, \-t tty 할당) 56 | 57 | ```bash 58 | $$ docker run -it appleholic/pytorchmnistexample:v0.1 python main.py 59 | ``` 60 | 61 |
62 | 63 | ### Docker HUB에 이미지 공유하기 64 | - Docker hub는 사람들이 빌드한 이미지를 공유하는 platform입니다. 빌드가 된 이미지는 주로 docker hub을 통해 공유되며, 이에 대해 간략하게 소개하고자 합니다. 65 | 66 |
67 | 68 | #### 계정 및 repository 만들기 69 | - [https://hub.docker.com/](https://hub.docker.com/) 에 자신의 아이디를 만듭니다. 70 | - 계정에 로그인 후 [https://hub.docker.com/add/repository/](https://hub.docker.com/add/repository/) 를 통해 repository를 만듭니다. 71 | 72 |
73 | 74 | #### 이미지 푸쉬를 위한 단계 75 | 76 | 1. docker login : docker hub에 로그인하여 해당 유저로 인증을 합니다. 아래 명령어 실행 시 username, password를 입력 받게 되고 로그인 성공 시 해당 유저 권한으로 이미지를 푸쉬할 수 있게 됩니다. 77 | ```bash 78 | $$ docker login 79 | ``` 80 | 81 | 2. docker push : local에서 만든 이미지의 이름과 remote repository의 이름이 같다면, 해당 remote repository로 아래와 같이 푸쉬할 수 있습니다. 82 | ```bash 83 | $$ docker push appleholic/pytorchmnistexample:v0.1 84 | ``` 85 | 86 | - 푸쉬가 완료되고 나면, 자신이 만들었던 repository에 push가 되었다는 기록과 위와 같이 태그를 추가하였으면, 태그 탭에서 확인이 가능합니다. 87 | ![pushed screenshot](/imgs/pushed_screenshot.png) 88 | 89 | - remote image로 테스트를 원할 시 아래와 같이 로컬 이미지를 지운 후 명령어를 재실행 해 보세요. 90 | 91 | ```bash 92 | $$ docker rmi appleholic/pytorchmnistexample:v0.1 -f 93 | $$ docker run -it appleholic/pytorchmnistexample:v0.1 python main.py 94 | ``` 95 | 96 |
97 | 98 | ### 기타 99 | 100 | - Docker 를 GPU에서 실행하기 위해서는 nvidia docker를 설치하여야 합니다. [https://github.com/NVIDIA/nvidia-docker](https://github.com/NVIDIA/nvidia-docker) 101 | - Docker로 자신이 실험한 코드를 이미지로 빌드하여 배포한다면, 다른 사람들은 docker만 설치되어 있다면 환경 세팅 등에 신경 쓸 필요 없이 코드 재현이 가능한 장점이 있습니다. 조금 더 신경써서 docker로 공유해보면 어떨까요 102 | - email : june.one@kakaobrain.com, choiilji@gmail.com 103 | -------------------------------------------------------------------------------- /imgs/pushed_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AppleHolic/PytorchDockerExample/c596a7c97012e29304379ee8b024b636c6e3d105/imgs/pushed_screenshot.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # Example from https://github.com/pytorch/examples/blob/master/mnist/main.py 2 | from __future__ import print_function 3 | import argparse 4 | import torch 5 | import torch.nn as nn 6 | import torch.nn.functional as F 7 | import torch.optim as optim 8 | from torchvision import datasets, transforms 9 | 10 | 11 | class Net(nn.Module): 12 | def __init__(self): 13 | super(Net, self).__init__() 14 | self.conv1 = nn.Conv2d(1, 10, kernel_size=5) 15 | self.conv2 = nn.Conv2d(10, 20, kernel_size=5) 16 | self.conv2_drop = nn.Dropout2d() 17 | self.fc1 = nn.Linear(320, 50) 18 | self.fc2 = nn.Linear(50, 10) 19 | 20 | def forward(self, x): 21 | x = F.relu(F.max_pool2d(self.conv1(x), 2)) 22 | x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2)) 23 | x = x.view(-1, 320) 24 | x = F.relu(self.fc1(x)) 25 | x = F.dropout(x, training=self.training) 26 | x = self.fc2(x) 27 | return F.log_softmax(x, dim=1) 28 | 29 | 30 | def train(args, model, device, train_loader, optimizer, epoch): 31 | model.train() 32 | for batch_idx, (data, target) in enumerate(train_loader): 33 | data, target = data.to(device), target.to(device) 34 | optimizer.zero_grad() 35 | output = model(data) 36 | loss = F.nll_loss(output, target) 37 | loss.backward() 38 | optimizer.step() 39 | if batch_idx % args.log_interval == 0: 40 | print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format( 41 | epoch, batch_idx * len(data), len(train_loader.dataset), 42 | 100. * batch_idx / len(train_loader), loss.item())) 43 | 44 | 45 | def test(args, model, device, test_loader): 46 | model.eval() 47 | test_loss = 0 48 | correct = 0 49 | with torch.no_grad(): 50 | for data, target in test_loader: 51 | data, target = data.to(device), target.to(device) 52 | output = model(data) 53 | test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss 54 | pred = output.max(1, keepdim=True)[1] # get the index of the max log-probability 55 | correct += pred.eq(target.view_as(pred)).sum().item() 56 | 57 | test_loss /= len(test_loader.dataset) 58 | print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format( 59 | test_loss, correct, len(test_loader.dataset), 60 | 100. * correct / len(test_loader.dataset))) 61 | 62 | 63 | def main(): 64 | # Training settings 65 | parser = argparse.ArgumentParser(description='PyTorch MNIST Example') 66 | parser.add_argument('--batch-size', type=int, default=64, metavar='N', 67 | help='input batch size for training (default: 64)') 68 | parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N', 69 | help='input batch size for testing (default: 1000)') 70 | parser.add_argument('--epochs', type=int, default=10, metavar='N', 71 | help='number of epochs to train (default: 10)') 72 | parser.add_argument('--lr', type=float, default=0.01, metavar='LR', 73 | help='learning rate (default: 0.01)') 74 | parser.add_argument('--momentum', type=float, default=0.5, metavar='M', 75 | help='SGD momentum (default: 0.5)') 76 | parser.add_argument('--no-cuda', action='store_true', default=False, 77 | help='disables CUDA training') 78 | parser.add_argument('--seed', type=int, default=1, metavar='S', 79 | help='random seed (default: 1)') 80 | parser.add_argument('--log-interval', type=int, default=10, metavar='N', 81 | help='how many batches to wait before logging training status') 82 | args = parser.parse_args() 83 | use_cuda = not args.no_cuda and torch.cuda.is_available() 84 | 85 | torch.manual_seed(args.seed) 86 | 87 | device = torch.device("cuda" if use_cuda else "cpu") 88 | 89 | kwargs = {'num_workers': 1, 'pin_memory': True} if use_cuda else {} 90 | train_loader = torch.utils.data.DataLoader( 91 | datasets.MNIST('../data', train=True, download=True, 92 | transform=transforms.Compose([ 93 | transforms.ToTensor(), 94 | transforms.Normalize((0.1307,), (0.3081,)) 95 | ])), 96 | batch_size=args.batch_size, shuffle=True, **kwargs) 97 | test_loader = torch.utils.data.DataLoader( 98 | datasets.MNIST('../data', train=False, transform=transforms.Compose([ 99 | transforms.ToTensor(), 100 | transforms.Normalize((0.1307,), (0.3081,)) 101 | ])), 102 | batch_size=args.test_batch_size, shuffle=True, **kwargs) 103 | 104 | model = Net().to(device) 105 | optimizer = optim.SGD(model.parameters(), lr=args.lr, momentum=args.momentum) 106 | 107 | for epoch in range(1, args.epochs + 1): 108 | train(args, model, device, train_loader, optimizer, epoch) 109 | test(args, model, device, test_loader) 110 | 111 | 112 | if __name__ == '__main__': 113 | main() 114 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | torchvision --------------------------------------------------------------------------------