├── .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 | [](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 | 
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
--------------------------------------------------------------------------------