Gorio Tech Blog search

GAN(Generative Adversarial Networks), GAN 논문 설명

|

이 글에서는 2014년 6월 Ian J. Goodfellow 등이 발표한 Generative Adversarial Networks(GAN, 생성적 적대신경망)를 살펴보도록 한다.

간단히 GAN은 두 가지 모델을 동시에 학습시키는 구조이다. G(Generator, 생성자)라는 모델은 직접 볼 수 없는 진짜 데이터와 최대한 비슷하게 생긴 가짜 데이터를 만드려고 하고, D(Distriminator, 식별자 또는 감별자)라는 모델은 자신에게 주어진 데이터가 진짜 데이터인지 가짜 데이터인지 최대한 구분하려고 한다.

GAN을 도식화한 구조는 다음과 같다. 출처

논문에서는 설명을 위한 예시로 화폐 위조범($G$)와 경찰($D$)을 제시하였다. 다만 차이가 있다면,

  • 위조범은 진짜를 볼 수 없다는 것(그래서 장님blind라 불린다)
  • 경찰은 자신이 판별한 결과를 위조범에게 알려준다 는 것이 있다.

참고: $G$로 들어가는 입력 벡터를 뜻하는 noise는 latent variable이라고도 하며, Auto-encoder에서 출력 영상을 만들기 위한 source와 비슷하기에 이 표현도 사용된다.
또 GAN은 특정한 모델 구조를 가진 것이 아니므로 코드가 특별히 정해진 것은 아니다.

논문을 적절히 번역 및 요약하는 것으로 시작한다. 많은 부분을 생략할 예정이므로 전체가 궁금하면 원 논문을 찾아 읽어보면 된다.


Generative Adversarial Networks(GAN)

논문 링크: Generative Adversarial Networks

초록(Abstract)

이 논문에서는 적대적으로 동작하는 두 생성 모델을 동시에 학습시키는 새 framework를 제안한다. 생성자 G는 원본 data distribution을 흉내내려 하고, D는 눈앞의 데이터가 G에게서 온 것인지를 판별한다. G의 목적은 D가 최대한 실수하게 만드는 것이고, D는 당연히 최대한 정확하게 진짜/가짜를 판별하는 것이다.
이는 2인 minimax 게임과 비슷하다. 어떤 유일한 해가 존재하여 최종적으로 D는 실수할 확률이 0.5가 된다(즉 찍는 수준).
G와 D가 multi-layer perceptron으로 구성되면 전체 시스템은 backpropagation으로 학습될 수 있다.
GAN에는 어느 과정에서든 마르코프 체인이나 기타 다른 네트워크가 필요가 전혀 없다.


서론(Introduction)

적대적인 두 네트워크를 학습시킨다. D는 원본 data distribution인지 G에서 온 것인지를 판별하고, G는 D가 실수하도록 가짜 데이터를 잘 만들어내는 것이 목표이다.
이 framework는 많은 특별한 학습 알고리즘과 optimizer를 사용할 수 있다. 앞서 말한 대로 multi-layer perception을 쓰면 다른 복잡한 네트워크는 필요 없이 오직 forward/backpropagation만으로 (이 논문에서는 dropout을 또 쓴다) 학습이 가능하다.


관련 연구(Related Works)

궁금하면 읽어보자.

  • RBMs: restricted Boltzmann machines, 잠재 변수를 가진 유향 그래프 모델에 대한 대안으로, 무향 그래프 모델
  • DBMs: deep Boltzmann machines, RBMs와 비슷함. 다양한 변형이 존재
  • MCMC: Markov chain Monte Carlo methods, 위 모델의 측정 방법
  • DBNs: Deep belief networks, 하나의 무향 레이어와 여러 유향 레이어의 hybrid 모델. 계삭적 문제가 있음
  • NCE: noise-contrasive estimation, log-likelihood를 근사하거나 경계값을 구하지 않는 방법
  • GSN: generative stochastic network, 확률분포를 명시적으로 정의하지 않고 분포 샘플을 생성하도록 학습시키는 방법을 사용
  • adversarial nets: 적대적 망은 생성 중 feedback loop를 필요로 하지 않아 sampling에서 Markov chain이 필요가 없다. 이는 backpropagation 성능 향상으로 이어진다.
  • auto-encoding varitional Bayes와 stochastic backpropagation은 생성 머신을 학습시키는 방법들 중 하나이다.

적대적 망(Adversarial nets)

기호 설명
$x$ 데이터
$p_g$ $x$에 대한 생성자의 분포
$p_z(z)$ input noise 변수
$\theta_g$ multilayer perceptrions의 parameters
$G$ $\theta_g$에 의해 표현되는 미분가능한 함수
$G(z; \theta_g$) data space에 대한 mapping
$D(x)$ $x$가 $p_g$가 아니라 원본 데이터에서 나왔을 확률
$D(x; \theta_d)$ 두 번째 multilayer perceptron

D의 목적은 데이터가 ‘원본’인지 ‘G가 생성한 데이터’인지 판별하는 것이므로 어떤 데이터에 대해 정확한 label(‘원본’ 또는 ‘G로부터’)을 붙이는 것이다. G의 목적은 D가 실수하게 만드는 것, 즉 어떤 데이터가 주어졌을 때 D가 ‘원본’이라고 판별할 확률과 ‘G로부터 나온 데이터’라고 판별할 확률을 모두 높이는 것(정확히는 같게)이다.
즉 $log(1-D(G(z)))$를 최소화하도록 G를 훈련시킨다.

D와 G 모두에 대해 value function $V(G, D)$를 정의하면,

[min_G max_D V(D, G) = \mathbb{E}{x \sim p{data}(x)}[log D(x)] + \mathbb{E}{x \sim p{z}(z)}[log (1-D(G(z)))]]

위 식의 의미는,

  • $min_G$: G는 V를 최소화하려고 한다.
  • $max_D$: D는 V를 최대화하려고 한다. 2-player minimax 게임과 같으므로 당연하다.
  • $\mathbb{E}$: 기댓값
  • $x \sim p_{data}(x)$: $x$가 원본 데이터 분포에서 왔을 때

D가 아주 똑똑한 경찰이라면, $x$가 실제로 원본에서 온 것이라면 $D(x)=1$이 될 것이고, $G(z)$에서 온 것이라면 $D(G(z))=0$이 된다. 만약 G가 완벽한 위조범이 되었다면, $D(x) = {1 \over 2}$이다.
따라서 D의 입장에서 V의 최댓값은 0이 되며, G의 입장에서 V의 최솟값은 $-\infty$임을 알 수 있다.

학습시킬 때, inner loop에서 D를 최적화하는 것은 매우 많은 계산을 필요로 하고 유한한 데이터셋에서는 overfitting을 초래하기 때문에, $k$ step만큼 D를 최적화하고 G는 1 step만 최적화하도록 한다.
학습 초반에는 G가 형편없기 때문에 D는 진짜인지 G가 생성한 것인지를 아주 잘 구분해 낸다.
또 G가 $log(1-D(G(z)))$를 최소화하도록 하는 것보다는 $log(D(G(z)))$를 최대화하도록 하는 것이 더 학습이 잘 된다. 이는 G가 형편없을 때는 $log(1-D(G(z)))$의 gradient를 계산했을 때 너무 작은 값이 나와 학습이 느리기 때문이라고 한다.

파란 점선은 disctiminative distribution(D), 검정색은 원본 데이터($p_x$), 초록색은 생성된 분포$p_g$(G), $x$는 원본 데이터 분포를, 화살표는 $x=G(z)$ mapping을 나타낸다. (a) 초기 상태. (b) D 학습 후, (c) G 학습 후, 분포가 비슷해지는 것을 볼 수 있다. (d) 여러 번의 학습 끝에 G가 완전히 원본을 흉내낼 수 있는 경지에 도달함. 즉 $p_g = p_{data}$. D는 이제 진짜인지 가짜인지 구분할 수 없다. 즉 $D(x) = {1 \over 2}$.


이론적 결과(Theoretical Results)

수학을 좋아한다면 직접 읽어보자.

  • Algorithm 1
    • for epochs do
      • for k steps do
        • noise prior $p_g(z)$로부터 $m$개의 noise sample $z^{(1)}, …, z^{(m)}$을 뽑는다.
        • noise prior $p_{data}(x)$로부터 $m$개의 noise sample $x^{(1)}, …, x^{(m)}$을 뽑는다.
        • D를 다음 stochastic gradient로 update한다. (ascending)
          • $ \nabla_{\theta_d} {1 \over m} \sum^m_{i=1} [log D(x^{(i)}) + log (1-D(G(z^{(i)})))] $
      • noise prior $p_g(z)$로부터 $m$개의 noise sample $z^{(1)}, …, z^{(m)}$을 뽑는다.
      • G를 다음 stochastic gradient로 update한다. (descending)
        • $ \nabla_{\theta_d} {1 \over m} \sum^m_{i=1} [log (1-D(G(z^{(i)})))] $
  • 이 minimax 게임은 $p_g = p_{data}$에 대한 global optimum을 가진다.
    • G를 고정했을 때, optimal한 D는 다음과 같다.

[D^*G(x) = {p{data}(x) \over p_{data}(x) + p_g(x)}]

  • Algorithm 1은 수렴한다.

실험(Experiments)

MNIST, Toronto Face Database(TFD), CIFAR-10에 대해 학습을 진행했다.

  • G는 rectifier linear activations와 sigmoid를 사용했고, D는 maxout activations를 사용했다.
  • Dropout은 D를 학습시킬 때 사용했다.
  • noise는 G에서 가장 밑의 레이어에만 input으로 넣었다.

자세한 실험 조건은 직접 읽어보자.

가장 오른쪽 열은 바로 옆에 있는 생성된 이미지와 가장 비슷한 학습 샘플이다. a) MNIST b) TFD c) CIFAR-10(fully connected model) d) CIFAR-10(convolutional D와 “deconvolutional” G)

숫자 간 보간을 했을 때는 위와 같이 된다. 물론 GAN을 통해 생성한 것이다.


장단점(Advantages and disadvantages)

단점

  • $p_g(x)$가 명시적으로 존재하지 않는다.
  • D는 G와 균형을 잘 맞추어서 성능이 향상되어야 한다(G는 D가 발전하기 전 너무 발전하면 안 된다).

장점

  • 마르코프 체인이 전혀 필요 없이 backprop만으로 학습이 된다.
  • 특별히 어떤 추론(inference)도 필요 없다.
  • 다양한 함수들이 모델에 접목될 수 있다.
  • 마르코프 체인을 썼을 때에 비해 훨씬 선명한(sharp) 이미지를 결과로 얻을 수 있다.

결론 및 추후 연구(Conclusions and future work)

  1. conditional generative model로 발전시킬 수 있다(CGAN).
  2. Learned approximate inference는 $x$가 주어졌을 때 $z$를 예측하는 보조 네트워크를 학습함으로써 수행될 수 있다.
  3. parameters를 공유하는 조건부 모델을 학습함으로써 다른 조건부 모델을 대략 모델링 할 수 있다. 특히, deterministic MP-DBM의 stochastic extension의 구현에 대부분의 네트워크를 쓸 수 있다.
  4. Semi-supervised learning에도 활용 가능하다. classifier의 성능 향상을 꾀할 수 있다.
  5. 효율성 개선: G와 D를 조정하는 더 나은 방법이나 학습하는 동안 sample $z$에 대한 더 나은 distributions을 결정하는 등의 방법으로 속도를 높일 수 있다.

참고문헌(References)

논문 참조!


보충 설명

목적함수

D의 목적함수는 G를 고정한 채로 진짜 데이터 $m$개와 가짜 데이터 $m$개를 D에 넣고, G에 대한 V를 계산한 뒤 gradient를 구하고 V를 높여 D를 최종적으로 업데이트한다.

[max_D V(D) = {1 \over m } \sum^m_{i=1} log D(x^i) + {1 \over m } \sum^m_{i=1} log D(1 - D(G(z^i)))]

G의 목적함수는 D를 고정한 채로 가짜 데이터 $m$개를 생성해 V을 계산한 뒤, G에 대한 V의 gradient를 계산하고 V를 낮춰 G를 업데이트한다.
G의 목적함수는 gradient가 0에 가까워지는 것을 막기 위해 논문에서 언급된 팁을 반영한 것이다.

[min_G V(G) = {1 \over m} \sum^m_{j=1} log(D(G(z^j)))]

목적함수 최적화의 의미

Machine Learning 관점에서 보면 모델이 loss가 최소화되는 parameter를 찾아가는 과정이다.
또는 진짜 데이터의 분포와 G가 생성한 가짜 데이터 분포 사이의 차이를 줄이는 것과도 같다.

수학적으로는 D가 이미 최적이라는 가정 하에, GAN이 목적함수를 최적화한다는 과정($p_{data}$와 $p_g$를 똑같이 만드려는 것)은 $p_{data}$와 $p_g$ 사이의 Jensen-Shannon divergence(JSD)를 최소화하는 것과 같다.
JSD는 Kullback–Leibler divergence의 대칭(symmetrized and smoothed) 버전이다. 그래서 GAN은 KLD를 최소화하는 것이라고 말하기도 한다.

분포 $P$와 $Q$에 대해, $KLD = D(P \Vert Q), M = {1 \over 2}(P+Q)$라 할 때, JSD는

[JSD(P \Vert Q) = {1 \over 2} D(P \Vert M) + {1 \over 2} D(Q \Vert M)]

이다.


학습 방법

GAN은 서로 경쟁하는 두 가지 모델을 학습시킨다. GAN을 쓰려면 다음 방법을 따른다.

  1. 우선 다음을 정의한다.
    1. R(Real): 실제 데이터. 논문에선 $x$로 표시
    2. I(Input 또는 Imaginary): G가 가짜 데이터를 생성할 source. 논문에선 $z$로 표시.
      • $G(z)$는 $G$가 $z$를 입력으로 받아 생성한 가짜 데이터이다.
    3. $G$(generator): 생성자, 위조범
    4. $D$(Distriminator): 감별자 또는 식별자, 경찰
  2. 다음 전체 과정을 num_epochs 동안 반복한다:
    1. D를 training하는 과정(d_steps만큼 반복): D와 G를 모두 사용은 하지만 D의 parameter만 업데이트한다.
      1. $D$에 실제 데이터($x$)와 정답(1)을 입력으로 주고 loss를 계산한다.
      2. $D$에 가짜 데이터($G(z)$)와 정답(0)을 입력으로 주고 loss를 계산한다.
      3. 두 loss를 합친 후 $D$의 parameter를 업데이트한다.
    2. G를 training하는 과정(g_steps만큼 반복): D와 G를 모두 사용은 하지만 G의 parameter만 업데이트한다.
      1. $D$에 가짜 데이터($G(z)$)와 정답(1)을 입력으로 주고 loss를 계산한다.
      2. 계산한 loss를 이용하여 $G$의 parameter를 업데이트한다.

단점 및 극복방안

GAN 논문에서는 수학적인 증명이 포함되어 있지만(최소 해를 가지며, 충분히 학습할 시 항상 그 해답을 찾는다), 여러 요인들로 인해 실제 학습시킬 때에는 학습이 좀 불안정하다는 단점이 있다.

Mode Collapsing

간단히 이 현상은 학습 모델이 실제 데이터의 분포를 정확히 따라가지 못하고 그저 뭉뚱그리기만 하면서 다양성을 잃어버리는 것이다.
예를 들면 1~9까지의 숫자 9개를 만드는 대신 5만 9개 만드는 것과 비슷하며, MNIST의 경우 10종류의 모든 숫자가 아닌 특정 숫자들만 생성하는 경우이다.

이는 GAN이 단순히 목적함수의 loss만을 줄이려는 방향으로 설정되어 있어 생기는 현상이다. 이 현상은 GAN의 개선 모델들에서 대부분 해결된다.

Oscillation

G와 D가 수렴하지 않고 진동하는 모양새를 보일 때가 있다. 이 역시 비슷한 이유로 발생하며, 나중 모델들에서 해결된다.

G와 D 사이의 Imbalance

학습을 진행하면 처음에는 D가 발전하고 나중에 G가 급격히 학습되는 형상을 보이는데, 처음부터 D가 너무 성능이 좋아져버리면 오히려 G가 학습이 잘 되지 않는 문제가 발생한다(D가 시작부터 G의 기를 죽이는 셈).

해결방안

  • 진짜 데이터와 가짜 데이터 간 Least Square Error를 목적함수에 추가한다(LSGAN).
  • 모델의 구조를 convolution으로 바꾼다(DCGAN)
  • mini-batch별로 학습을 진행할 경우 이전 학습이 잘 잊혀지는 것을 막기 위해 이를 기억하는 방향으로 학습시킨다.

튜토리얼

50줄로 짜보는 튜토리얼

원문 링크는 여기, 번역본은 여기에서 볼 수 있다.
해당 튜토리얼에서는

  1. 이 전체 과정을 num_epochs(여기서는 5000)만큼 반복한다.
    1. training D(d_steps만큼 반복):
      1. 가우시안 분포를 따르는 데이터를 Real Data로 생성하고
      2. 그 momentum(mean, std, skews, kurtoses)를 계산하여 D에게 전달, error를 계산한다.
      3. 또 Fake data를 G가 생성하게 하고
      4. D가 error를 계산하게 한다.
      5. 위 두 과정(1~2, 3~4)으로 D의 parameter를 업데이트한다.
    2. training G(g_steps만큼 반복):
      1. G로 Fake data를 생성한다.
      2. D에게서 판별 결과를 받아온다.
      3. G가 error를 계산하게 한다.
      4. G의 parameter를 업데이트한다.

코드는 원문에도 소개되어 있지만 전체는 사실 186줄이다(…) 물론 GAN의 핵심 코드는 50줄 정도이다.

MNIST 튜토리얼

GAN의 핵심 부분을 제외한 부분은 여기를 참고하면 된다.

우선 기본 설정부터 하자.

import torch
import torch.nn as nn
from torch.optim import Adam
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

import argparse

from matplotlib import pyplot as plt
import numpy as np

import pickle
import os
import imageio


parser = argparse.ArgumentParser(description='GAN tutorial: MNIST')

parser.add_argument('--epochs', type=int, default=100, help='number of epochs')
parser.add_argument('--batch-size', type=int, default=64, help='size of mini-batch')
parser.add_argument('--noise-size', type=int, default=100, help='size of random noise vector')
parser.add_argument('--use-cuda', type=bool, default=True, help='use cuda if available')
parser.add_argument('--learning-rate', '-lr', type=float, default=0.0002, help='learning rate of AdamOptimizer')
parser.add_argument('--beta1', type=float, default=0.5, help='parameter beta1 of AdamOptimizer')
parser.add_argument('--beta2', type=float, default=0.999, help='parameter beta2 of AdamOptimizer')
parser.add_argument('--output-dir', type=str, default='output/', help='directory path of output')
parser.add_argument('--log-file', type=str, default='log.txt', help='filename of logging')

args = parser.parse_args()

os.makedirs(args.output_dir, exist_ok=True)

use_cuda = args.use_cuda and torch.cuda.is_available()

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(mean=(0.5,), std=(0.5,))
])

mnist = datasets.MNIST(root='data', download=True, transform=transform)
dataloader = DataLoader(mnist, batch_size=args.batch_size, shuffle=True)

Generator는 다음과 같이 선언한다. 레이어는 총 4개, activation function은 LeakyRELU와 Tanh를 사용하였다.

class Generator(nn.Module):
    
    def __init__(self):
        super(Generator, self).__init__()
        self.linear1 = nn.Linear(in_features=100, out_features=256)
        self.linear2 = nn.Linear(in_features=256, out_features=512)
        self.linear3 = nn.Linear(in_features=512, out_features=1024)
        self.linear4 = nn.Linear(in_features=1024, out_features=28 ** 2)
    
    
    def forward(self, x):
        """
        :param x: input tensor[batch_size * noise_size]
        :return: output tensor[batch_size * 1 * 28 * 28]
        """
        x = nn.LeakyReLU(0.2)(self.linear1(x))
        x = nn.LeakyReLU(0.2)(self.linear2(x))
        x = nn.LeakyReLU(0.2)(self.linear3(x))
        x = nn.Tanh()(self.linear4(x))
        return x.view(-1, 1, 28, 28)

Discriminator는 다음과 같다. Linear Layer는 G의 역방향으로 가는 것과 비슷하지만, activation function에는 차이가 있다.

class Discriminator(nn.Module):
    
    def __init__(self):
        super(Discriminator, self).__init__()
        self.linear1 = nn.Linear(in_features=28 ** 2, out_features=1024)
        self.linear2 = nn.Linear(in_features=1024, out_features=512)
        self.linear3 = nn.Linear(in_features=512, out_features=256)
        self.linear4 = nn.Linear(in_features=256, out_features=1)
    
    
    def forward(self, x):
        """
        :param x: input tensor[batch_size * 1 * 28 * 28]
        :return: possibility of that the image is real data
        """
        x = x.view(-1, 28 ** 2)
        x = nn.LeakyReLU(0.2)(self.linear1(x))
        x = nn.Dropout()(x)
        x = nn.LeakyReLU(0.2)(self.linear2(x))
        x = nn.Dropout()(x)
        x = nn.LeakyReLU(0.2)(self.linear3(x))
        x = nn.Dropout()(x)
        return nn.Sigmoid()(self.linear4(x))

GAN의 핵심 부분은 다음과 같다. 위의 Gaussian 분포 예제와 크게 다르지 않아서 크게 설명은 필요없을 듯 하다. 차이점을 조금 적어보자면

  1. 분포의 momentum을 G의 데이터 생성 source로 사용하는 대신 길이 100(MNIST의 경우 보통)짜리 random vector를 사용한다. G는 이 길이 100짜리 벡터를 갖고 MNIST의 숫자 이미지와 비슷한 이미지를 생성하려고 하게 된다.
  2. .cuda()DataLoader를 사용하는 것 정도가 있겠으나 GAN의 핵심 부분은 아니다.
    for epoch in range(args.epochs):
        for D_real_data, _ in dataloader:
            
            batch_size = D_real_data.size(0)
            
            # Training D with real data
            D.zero_grad()
            
            target_real = torch.ones(batch_size, 1)
            target_fake = torch.zeros(batch_size, 1)
            
            if use_cuda:
                D_real_data, target_real, target_fake = \
                D_real_data.cuda(), target_real.cuda(), target_fake.cuda()
            
            D_real_decision = D(D_real_data)
            D_real_loss = criterion(D_real_decision, target_real)
            
            # Training D with fake data
            
            z = torch.randn((batch_size, args.noise_size))
            if use_cuda: z = z.cuda()
            
            D_fake_data = G(z)
            D_fake_decision = D(D_fake_data)
            D_fake_loss = criterion(D_fake_decision, target_fake)
            
            D_loss = D_real_loss + D_fake_loss
            D_loss.backward()
            
            D_optimizer.step()
            
            # Training G based on D's decision
            G.zero_grad()
            
            z = torch.randn((batch_size, args.noise_size))
            if use_cuda: z = z.cuda()
            
            D_fake_data = G(z)
            D_fake_decision = D(D_fake_data)
            G_loss = criterion(D_fake_decision, target_real)
            G_loss.backward()
            
            G_optimizer.step()

전체 코드는 여기를 참조하라.


이후 연구들

사실 2014년 발표된 original GAN은

  • 학습이 불안정하고
  • 고해상도 이미지는 생성하지 못하는 한계를 갖고 있었다. 논문에서 optimal point가 있고 그쪽으로 수렴한다는 것을 보였지만, 실제로는 여러 변수 때문에 학습이 항상 잘 되는 것이 아니라는 현상을 보인다. 이러한 문제를 보완하기 위해 GAN 이후로 수많은 발전된 GAN이 연구되어 발표되었다.

그 중에서 가장 중요한 것을 3가지 정도만 뽑자면

  1. Convolution을 사용하여 GAN의 학습 불안정성을 많이 개선시킨 DCGAN(Deep Convolutional GAN, 2015)
  2. 단순 생성이 목적이 아닌 원하는 형태의 이미지를 생성시킬 수 있게 하는 시초인 CGAN(Conditional GAN, 2014)
  3. GAN이 임의의 divergence를 사용하는 경우에 대해 local convergence함을 보여주고 그에 대해 실제 작동하는 GAN을 보여준 f-GAN(2016)

일 듯 하다.

많은 GAN들(catGAN, Semi-supervised GAN, LSGAN, WGAN, WGAN_GP, DRAGAN, EBGAN, BEGAN, ACGAN, infoGAN 등)에 대한 설명은 여기에서, DCGAN에 대해서는 다음 글에서 진행하도록 하겠다.


Comment  Read more

Python argparse 사용법

|

이 글에서는 Python 패키지인 argparse에 대해 알아본다. Machine Learning 코드를 볼 때 꽤 자주 볼 수 있을 것이다.


Import

import argparse

argparse

python train.py --epochs 50 --batch-size 64 --save-dir weights

Machine Learning을 포함해서, 위와 같은 실행 옵션은 많은 코드에서 볼 수 있었을 것이다. 학습 과정을 포함하여 대부분은 명령창 또는 콘솔에서 python 파일명 옵션들...으로 실행시키기 때문에, argparse에 대한 이해는 필요하다.

중요:

  • 기본적으로 argparse 라이브러리는 명령창(터미널)에서 실행하는 것을 원칙으로 한다. Jupyter notebook이나 (iPython) 대화형 실행 framework에서는 제대로 실행되지 않을 수 있다. 또한 이러한 대화형 framework에서는 코드 상에서 명시적으로 집어 넣는 게 아닌 이상 인자에 값을 바로 줄 수도 없다.
  • 그래도 쓰고 싶다면 args = parser.parse_args()args = parser.parse_args(args=[])로 바꾸고 사용할 수는 있다…하지만 위의 이유로 인해 별 의미는 없을 듯하다.

필자는 이 글에서 위의 명령 중 --epochs와 같은 것을 인자, 50과 같은 것을 (같이 준) 으로 부르겠다.

argparse는 python에 기본으로 내장되어 있다.

import argparse
import os

import os는 output directory를 만드는 등의 역할을 위해 필요하다.

argparse를 쓰려면 기본적으로 다음 코드가 필요하다.

import argparse

parser = argparse.ArgumentParser(description='Argparse Tutorial')
# argument는 원하는 만큼 추가한다.
parser.add_argument('--print-number', type=int, 
                    help='an integer for printing repeatably')

args = parser.parse_args()

for i in range(args.print_number):
    print('print number {}'.format(i+1))
  1. 일단 ArgumentParser에 원하는 description을 입력하여 parser 객체를 생성한다. description 외에도 usage, default value 등을 지정할 수 있다.
  2. 그리고 add_argument() method를 통해 원하는 만큼 인자 종류를 추가한다.
  3. parse_args() method로 명령창에서 주어진 인자를 파싱한다.
  4. args라는 이름으로 파싱을 성공했다면 args.parameter 형태로 주어진 인자 값을 받아 사용할 수 있다.

실행 결과

> python argparseTest.py -h
usage: argparseTest.py [-h] [--print-number PRINT_NUMBER]

Argparse Tutorial

optional arguments:
  -h, --help            show this help message and exit
  --print-number PRINT_NUMBER
                        an integer for printing repeatably

> python argparseTest.py --print-number 5
print number 1
print number 2
print number 3
print number 4
print number 5

argparse의 인자는 지정할 수 있는 종류가 상당히 많다.

–help, -h

--help 또는 -h: 기본으로 내장되어 있는 옵션이다. 이 인자를 넣고 python으로 실행하면 인자 사용법에 대한 도움말이 출력된다.

> python argparseTest.py -h
usage: argparseTest.py [-h] [--print-number PRINT_NUMBER]
...

argument 이름 정의

인자의 이름을 지정할 때 여러 이름을 짓는 것이 가능하다. 지정할 때 두 개를 연속해서 나열한다. 보통 1~2개를 지정하는데, --help-h같이 fullname과 약자를 하나씩 지정하는 편이다. 또 help=에서 description을 써줄 수 있다.
참고로 help 메시지는 % formatting을 지원한다.

parser.add_argument('--print-number', '-p', help='an integer for printing repeatably')

type 지정

기본적으로 parse_args()가 주어진 인자들을 파싱할 때는 모든 문자를 숫자 등이 아닌 문자열 취급한다. 따라서 데이터 타입을 지정하고 싶으면 add_argument()에서 type=을 지정해 주어야 한다. default는 말한 대로 str이다.

  • ex) parser.add_argument('--print-number', '-p', type=int, ...)
  • type으로 사용 가능한 것은 한 개의 문자열을 받아들여 return 문이 있는 모든 callable 객체이다.
  • Common built-in types과 functions이 사용 가능한데, str, int, float, boolopen 등이 있다. list와 같은 것은 불가능하다. list처럼 쓰고 싶으면 아래쪽에서 설명할 action=append를 이용한다.
  • argparse.FileType() 함수도 type=에 사용 가능한데, mode=, bufsize=, encoding=, errors= parameter를 취하는 함수로서 다양한 파일을 여러 옵션으로 지정할 수 있다. 예를 들어 argparse.FileType('w')는 쓰기 가능한 파일을 만든다. 자세한 것은 여기를 참조한다.

positional / optional 인자

positional 인자와 optional 인자가 있다. 인자의 이름 앞에 -가 붙어 있으면 optional, 아니면 positional 인자로서 필수로 지정해야 한다.
단, positional 인자도 필수로 넣어야 하게끔 할 수 있다. add_argument() 함수에 required=True를 집어넣으면 된다. 그러나 C언어에서 #define true false같은 짓인 만큼 권장되지 않는다.

# argparseTest.py
# ...
parser.add_argument('--foo', '-f') # optional
parser.add_argument('bar')         # positional
args = parser.parse_args()
print('args.foo:', args.foo)
print('args.bar:', args.bar)
# optional 인자는 지정하지 않아도 되고, 그럴 경우 기본값이 저장된다.
> python argparseTest.py bar_value
args.foo: None
args.bar: bar_value

# positional 인자는 반드시 값을 정해 주어야 한다.
> python argparseTest.py --foo 1
usage: argparseTest.py [-h] [--foo FOO] bar
argparseTest.py: error: the following arguments are required: bar

# optional 인자 뒤에는 반드시 저장할 값을 지정해야 한다. 
# 이는 `action=store`인 optional 인자에 해당한다. 6번 항목에서 설명하겠다.
> python argparseTest.py bar_value --foo
usage: argparseTest.py [-h] [--foo FOO] bar
argparseTest.py: error: argument --foo/-f: expected one argument

# optional 인자는 `--foo 3`또는 `--foo=3` 두 가지 방식으로 지정할 수 있다.
# positional 인자는 그런 거 없다.
> python argparseTest.py --foo=5 bar=bar_value
args.foo: 5
args.bar: bar_value

# positional 인자가 여러 개라면 순서를 반드시 지켜야 한다.
# optional 인자는 값만 잘 지정한다면 어디에 끼워 넣어도 상관없다.
> python argparseTest.py bar_value --foo 7
args.foo: 7
args.bar: bar_value

default 값 지정

값을 저장할 때 명시적으로 지정하지 않았을 때 들어가는 기본값을 설정할 수 있다. add_argument()에서 default= 옵션을 지정한다. - argparse.SUPPRESS를 적을 경우, 인자를 적지 않았을 때 None이 들어가는 것이 아닌 아예 인자 자체가 생성되지 않는다. 또한 --help에도 표시되지 않는다.

parser.add_argument('--foo', '-f', type=int, default=5)
> python argparseTest.py
args.foo: 5

# 그러나 인자를 적어 놓고 값은 안 주면 에러가 난다. 
# 기본적으로 한 개의 값을 추가로 받아야 하기 때문이다.
# 이걸 바꾸려면 6번이나 7번 항목을 참조한다.
> python argparseTest.py --foo
usage: argparseTest.py [-h] [--foo FOO]
argparseTest.py: error: argument --foo/-f: expected one argument

action의 종류 지정

인자를 정의(add_argument()에 의해)할 때 action을 지정할 수 있다. 액션에는 다음과 같은 것들이 있으며, 기본값은 store이다.

  • store: action을 지정하지 않으면 store이 된다. 인자 이름 바로 뒤의 값을 해당 인자에 대입(저장)시킨다.
  • store_const: add_argument()에서 미리 지정되어 있는 const=에 해당하는 값이 저장된다. const=는 반드시 써 주어야 한다.
  • store_true, store_false: 인자를 적으면(값은 주지 않는다) 해당 인자에 TrueFalse가 저장된다.
  • append: 값을 하나가 아닌 여러 개를 저장하고 싶을 때 쓴다. 인자를 여러 번 호출하면 같이 주는 값이 계속 append된다.
  • append_const: append와 비슷하지만 사전에 지정한 const 값이 저장된다.
  • count: 인자를 적은 횟수만큼 값이 올라간다. 보통 verbose 옵션에 많이 쓴다.
  • help: 도움말 메시지를 출력하게 하고 종료하여 코드는 실행시키지 않는다. --help 역할을 대신한다.
  • version: version 인자에 사용가능하다. 버전 정보를 출력하고 종료한다.
parser.add_argument('--foo', action='store_const', const=10)
> python argparseTest.py --foo
args.foo: 10

# 인자를 적지 않으면 default 값(None)이 저장된다.
parser.add_argument('--foo', action='store_const', const=10)
> python argparseTest.py
args.foo: None

# default 값을 지정하면 당연히 바뀐다.
parser.add_argument('--foo', action='store_const', const=10, default=5)
> python argparseTest.py
args.foo: 5

# store_true의 경우 default 값은 false이며, 인자를 적어 주면 true가 저장된다.
# store_false의 경우 반대이다.
parser.add_argument('--foo1', action='store_true')
parser.add_argument('--foo2', action='store_true')
parser.add_argument('--foo3', action='store_false')
parser.add_argument('--foo4', action='store_false')
args = parser.parse_args()

print('args.foo1:', args.foo1)
print('args.foo2:', args.foo2)
print('args.foo3:', args.foo3)
print('args.foo4:', args.foo4)
> python argparseTest.py --foo1 --foo4
args.foo: True
args.foo: False
args.foo: True
args.foo: False

# 참고로 한 번만 호출해도 args.foo는 데이터 타입이 list가 된다. 안 하면 None이다.
parser.add_argument('--foo', action='append')
> python argparseTest.py --foo 1 --foo 123 --foo=xyz
args.foo: ['1', '123', 'xyz']

attribute name: -, _ 구분

인자의 이름에는 -_을 쓸 수 있다. 단, python 기본 문법은 변수명에 -를 허용하지 않기 때문에, 인자의 이름에 -가 들어갔다면 args.인자로 접근하려면 -_로 바꿔 주어야 한다.

  • --print-number의 경우 args.print_number로 접근할 수 있다.
  • --print_number의 경우 args.print_number로 동일하다.

dest: 적용 위치 지정

argument를 지정할 때 store나 action의 저장 또는 적용 위치를 바꿔서 지정할 수 있다. 예를 들어 --foodest= 옵션을 --foo-list로 지정하면, args.foo_list에 값이 저장되는 식이다.

parser.add_argument('--foo', action='append', dest='foo_list')
parser.add_argument('--bar', dest='bar_value')
args = parser.parse_args()

print('args.foo_list:', args.foo_list)
print('args.bar_value:', args.bar_value)

try:
    if args.foo is not None:
        print('Hmm?')
except AttributeError as e:
    print('Where are you gone?', e)
> python argparseTest.py --foo 1 --foo 123 --foo=xyz --bar ABC
args.foo_list: ['1', '123', 'xyz']
args.bar_value: ABC
Where are you gone? 'Namespace' object has no attribute 'foo'

nargs: 값 개수 지정

argparse는 일반적으로 1개의 값을 추가로 받거나, action=store_true의 경우는 값을 추가로 받지 않는다. 이를 바꿔 주는 것이 nargs= 이다.

  • N: N개의 값을 읽어들인다.
  • ?: 0개 또는 1개의 값을 읽어들인다.
    • 인자와 값을 모두 적은 경우 해당 값이 저장된다.
    • 인자만 적은 경우 const 값이 저장된다.
    • 아무것도 적지 않았으면 default 값이 저장된다.
  • *: 0개 이상의 값을 전부 읽어들인다.
  • +: 1개 이상의 값을 전부 읽어들인다. 정규표현식의 것과 매우 비슷하다.
  • argparse.REMAINDER: 남은 값을 개수 상관없이 전부 읽어들인다.

예제는 원문이나 번역본을 참조한다.

choices: 값 범위 지정

인자와 같이 주어지는 값의 범위를 제한하고 싶으면 choices= 옵션을 쓰면 된다. choices=에 들어갈 수 있는 정의역은 list 등 iterable 객체이다(in 조건검사를 할 수 있으면 된다).

parser.add_argument('--foo', choices=range(1, 5))
> python argparseTest.py --foo 5
usage: argparseTest.py [-h] [--foo {1,2,3,4}]
argparseTest.py: error: argument --foo: 
invalid choice: '5' (choose from 1, 2, 3, 4)

metavar: 이름 재지정

metavar은 help=에서 도움말 메시지를 생성할 때 표시되는 이름을 변경할 수 있다(직접 값을 참조하는 args.foo 같은 경우 기본 이름 또는 dest=에 의해 재지정된 이름을 써야 한다).


References

Comment  Read more

PyCharm 사용법(파이참 설치 및 사용법)

|

PyCharm(파이참)은 Jetbrains 사에서 제작 및 배포하는 유료/무료 프로그램이다.
Professional 버전은 돈을 주고 구입하거나, 학생이라면 학생 인증을 하고 무료로 사용할 수 있다.

글이 길기 때문에 사용법을 검색하고 싶다면 Ctrl + F 키를 누른 다음 검색해 보자.

2023.03.07 updated


설치

PyCharm 홈페이지에서 설치 파일을 다운받는다.

또는 Jetbrains Toolbox를 받은 다음 PyCharm을 받아도 된다.

Windows, Mac, Linux

유료 버전을 구매했거나 학생 인증이 가능하다면, Professional(혹은 Education) 버전을 다운받도록 한다.

Settings

설치 시 다음 창을 볼 수 있다. 해당 컴퓨터에 설치한 적이 있으면 설정 파일 위치를 지정하고, 아니면 말도록 하자.

필자는 Darcula로 지정했고, 왼쪽 아래의 Skip Remaining and Set Defaults 버튼을 누른다. 본인이 추가 설정하고 싶은 부분이 있으면 이후 설정에서 마음대로 바꾸면 된다.

설정을 완료하면 아래와 같은 화면을 볼 수 있다. 오른쪽 아래의 Configure > Settings 를 클릭한다.

정확히는 Settings for New Projects라는 대화창을 볼 수 있다. 이는 새 프로젝트를 만들 때 적용되는 기본 설정이다. 새로운 설정을 만들고 싶다면 Default 설정을 복제(Duplicate)한 뒤 새 설정에서 바꾸도록 한다.

설정에서 Appearance & Behavior > Appearance에서, ThemeDarcula 또는 다른 것으로 지정할 수 있다. 아래의 Use Custom Font는 메뉴 등의 폰트를 해당 폰트로 지정할 수 있다.
참고로, 코드의 폰트는 Editor > Font에서 지정한다. 이 두 가지 역시 구분하도록 한다. 기본값은 Monospaced이다.

Keymap에서는 단축키를 지정할 수 있다. PyCharm의 기본 단축키는 타 프로그램과 좀 다른 부분이 많아 필자는 일부를 바꿨다.
변경하고 싶은 단축키를 찾아서 더블클릭 또는 우클릭하면 기존에 지정되어 있는 단축키를 삭제하고 새 단축키를 지정할 수 있다. 이때 겹친다면 기존 단축키를 남겨둘지 제거할지 선택할 수 있다. 또한 마우스와 조합한 단축키로 지정할 수도 있다.
그리고 검색창 옆에 돋보기 + 네모 3개로 이루어진 아이콘을 클릭하면 명령의 이름이 아닌 현재 지정되어 있는 단축키로 검색할 수 있다(예: Ctrl + W).

추천하는 변경할 단축키는 다음과 같다.
아래쪽은 필자가 지정하여 사용하는 단축키이다. 이외에도 유용한 기능을 몇 개 적어 놓았다.

Menu 변경 전 변경 후
Execute selection in console Alt + Shift + E Ctrl + Enter
Edit > Find > Replace Ctrl + H Ctrl + R
Refactor > Rename Shift + F6 F2
Other > Terminal   Alt + T
Other > Python Console   Alt + 8
Other > SciView   Alt + 0
Show in Explorer   Ctrl + Alt + Shift + E
Window > Editor Tabs > Close   Ctrl + W
     
Type Info   Ctrl + Alt + Button1 Click
Split and Move Right   Ctrl + Alt + Shift + R
Go to Declaration or Usages   Ctrl + Button1 Click

필자의 경우 나머지 설정은 그대로 두는 편이나, Ctrl + Enter로 바꿀 때는 다른 곳에 할당된 것을 지운다(Already assigned 경고창에서 Keep 대신 Remove를 선택). 안 그러면 선택한 부분이 Python Console(대화형)에서 실행되지 않는다.

위 그림에서 기본 Python Interpreter 파일(python.exe)를 설정한다. 새 프로젝트를 생성 시 Configure Python Interpreter라는 경고가 보이면서 코드 실행이 안 되면 인터프리터가 설정되지 않은 것이다. 컴퓨터에 설치된 파이썬 파일을 찾아 설정하자.

Show All...을 클릭하면 처음에는 빈 창이 보인다. +를 눌러서 원하는 환경을 추가한다. 기존의 것을 추가하거나, 새로운 가상환경(virtualenv 또는 conda)를 즉석에서 생성 가능하다.
이렇게 만든 가상환경은 해당 프로젝트에서만 쓰거나(기본 설정), 아래쪽의 Make available to all projects를 체크하여 다른 프로젝트에서도 해당 인터프리터를 택할 수 있도록 정할 수도 있다.

PyCharm에서 코드 실행을 대화형으로 하면 Python Console에 자꾸 Special Variables라는 창이 뜨는 것을 볼 수 있다. 보통 쓸 일이 없는데 기본으로 표시되는 것이므로, Build, Execution, Deployment > Console에서 Show console variable by default 체크를 해제한다.

해당 설정을 마쳤으면 첫 화면에서 Create New Project를 클릭한다.

프로젝트 이름은 기본적으로 Untitled 이므로 바꿔주고, 아래쪽의 Project Interpreter를 설정해 둔다. 미리 설정했다면 목록이 보일 것이고, 아니라면 새로 생성하거나 python.exe 위치를 찾아 지정해준다.

Sync Settings

2022.3 버전 기준으로 오른쪽 상단에 Settings Sync라는 기능이 생겼다. 클릭해보자(원래는 off로 되어 있을 것이다).

그리고 Enable Settings Sync...를 눌러 활성화한 다음 어떤 세팅을 동기화할지 선택하면 된다.

제일 아래쪽에는 지금과 같은 설정 동기화 방식을 PyCharm에만 적용할지, 다른 모든 Jetbrains 제품군에 적용할지 선택하는 설정이 들어 있다.


예전 버전 설정 방법은 아래에 설명하고 있다.

시작 화면에서 Configure > Settings Repository..., 또는 프로젝트 생성 후 File > Settings Repository... 를 클릭하면 지금까지 설정한 설정들을 git repository에 저장할 수 있다. git을 알고 있다면, Merge, Overwrite Local, Overwrite Remote의 뜻을 알 것이라 믿는다. git repository에 저장하면 컴퓨터를 옮겨도 동일한 설정을 쉽게 지정할 수 있다.

git repository는 그냥 여러분의 git 계정에서 빈 거 하나 만든 다음에 그 주소를 복사하면 된다. 그러면 PyCharm이 알아서 설정을 동기화시켜 줄 것이다.

이를 지정하려면 Personal Access Token이 필요하다. 여기를 참조한다.

등록이 완료되면 Merge, Overwrite Local(git에 저장된 내용을 local로 덮어씀), Overwrite Remote(현재 local 설정을 인터넷에 덮어씀) 중 하나를 선택해 설정을 동기화할 수 있다.

참고: 이렇게 동기화한 경우 일부 설정(예: kepmap 등)이 바로 적용되지 않는 경우가 있다. 그런 경우는

여기에서 Keymap 설정을 변경해 주면 된다. 보통 처음 동기화를 시도하면 기본 설정이나 어떤 Default Copy 버전으로 동작하고 있는 경우가 많다.

File Encoding 설정

코딩을 해 봤다면 알겠지만 한글이나 기타 UTF-8 인코딩 문자는 글자가 깨지는 경우가 흔하다. PyCharm의 기본 설정이 UTF-8이 아닌데, 이를 설정해주자.

아래 과정은 Settings을 열고 Encoding을 검색한 다음 나오는 모든 설정을 ‘UTF-8’로 바꾸는 과정과 같다.

모든 부분에서 글자가 안 깨지게 하려면 다음을 설정한다.

  • File > New Projects Settings > Settings for New Projects 메뉴로 들어가면 Settings for New Projects 창이 뜬다.
    • 여기서 Editor > File Encodings 메뉴로 들어간 다음 Global Encoding, Project Encoding, Project Files > Default Encoding for properties files 의 설정을 모두 UTF-8로 바꿔준다.
  • 이미 생성한 프로젝트에도 적용하려면 File > Settings로 대화창을 연 다음 같은 과정을 반복한다.

  • Help > Edit Custom VM Options... 메뉴를 클릭하면 <version>.vmoptions 파일이 열린다.

    • 여기서 다음을 파일의 끝에 추가하고 저장한다.
        -Dfile.encoding=UTF-8
        -Dconsole.encoding=UTF-8
      
    • 그리고 PyCharm을 재시작한다.

여기까지 설정했으면 파일이나 터미널 등에서 문자가 깨지지 않을 것이다. 그럼에도 깨지는 게 있으면 UTF-8 인코딩이 아니거나, 설정을 빠뜨렸거나..일 것이다. 위에서 설명한 대로 Settings에서 Encoding이라고 검색해 보자.

PyCharm 메모리 설정(Heap Memory)

간혹 PyCharm에 메모리가 너무 적게 할당되어 매우 느려지는 경우가 있다. 이 때도 위와 같이 Help > Edit Custom VM Options...를 클릭하여 설정 파일을 연다.

그러면 맨 위 두 줄은 다음과 같이 되어 있을 것이다.

-Xms128m
-Xmx750m
-XX:ReservedCodeCacheSize=240m

위의 숫자 128, 750, 240(megabytes)를 본인의 컴퓨터 사양에 맞추어 적당히 몇 배 곱해서 올려준다. 램이 8G라면 4G 이상은 안 해도 된다.

여기까지 초기 설정이 끝났다(원하는 부분만 진행해도 좋다). 이제 PyCharm 프로젝트 화면을 살펴보도록 하자.


Project 창(Alt + 1)

처음 프로젝트를 열면 다음과 같은 화면이 보일 것이다. (Show tips at startup은 무시한다)

맨 왼쪽에는 프로젝트 창이 있다. 맨 왼쪽 빨간 박스로 표시한 곳을 클릭하면 프로젝트 창을 접었다 폈다 할 수 있다. 단축키를 눌러도 된다(Alt + 1).

필자는 현재 untitled라는 이름으로 프로젝트를 생성했기 때문에, 루트 폴더는 현재 untitled이다. 주황 박스를 오른쪽 클릭하면 꽤 많은 옵션이 있다. 참고로 프로젝트 내 모든 디렉토리 또는 파일에 오른쪽 클릭하여 기능을 쓸 수 있다. 디렉토리를 우클릭했을 때와 파일을 우클릭했을 때 옵션이 조금 다르다.

각 옵션을 대략 설명하면,

  • New: File, Directory, Python File(.py), Jupyter Notebook(.ipynb) 등을 생성한다. 단축키 설정하는 방법은 다음과 같다.
    • 새 Python 파일을 생성할 때는 New > Python File을 선택하면 된다. 단축키를 설정하는 방법은 Settings > Keymap의 검색창에서 Python File을 검색하면 아무 단축키가 지정되어 있지 않은 것을 볼 수 있다. Add Keyboard Shortcut을 눌러 원하는 키를 설정해주자.
  • Cut, Copy, Paste 등은 설명하지 않겠다.
  • Copy Path, Copy Relative Path: 각각 해당 디렉토리 또는 파일의 절대/상대 경로를 복사한다. 이미지나 데이터 파일 등의 경로를 써야 할 때 유용하게 쓸 수 있다. 단, 사용 환경에 따라 디렉토리 구분자가 /, \, // 등으로 달라지는 경우가 있으니 주의.
  • Refactor: 해당 디렉토리 또는 파일의 이름을 변경한다. 이때 이 파일명을 사용하는 코드(file open 등)이 있으면 그 코드를 자동으로 수정하게 할 수 있다.
  • Find Usages: 해당 파일을 참조하는 코드를 살펴볼 수 있다. Refactor와 같이 사용하면 좋다.
  • Show in Explorer: 해당 디렉토리나 파일이 있는 디렉토리를 탐색기나 Finder 등에서 열 수 있다.
  • Mark Directory as: 디렉토리의 속성을 설정한다. 세부 옵션이 4개 있다.
    • **Sources Root: **프로젝트에서 코드의 최상위 폴더를 지정한다. 코드를 짜다 보면 프로젝트 루트 폴더에 직속된 파일이 아닌 경우 패키지나 파일 reference를 찾지 못하는 경우가 많은데, 그럴 때는 해당 코드를 포함하는 파일 바로 상위의 디렉토리를 Sources Root로 설정하면 빨간 줄이 사라지는 것을 볼 수 있다.
    • Excluded: PyCharm 색인(Index)에서 제외시킨다. PyCharm은 Find Usages와 같은 기능을 지원하기 위해 프로젝트 내 모든 파일과 코드에 대해 indexing을 수행하는데(목차를 생성하는 거랑 비슷함), 프로젝트 크기가 크면 굳이 필요 없는 수많은 파일들까지 indexing해야 한다. 이는 PyCharm 성능 저하와 함께 색인 파일의 크기가 매우 커지므로(임시 파일까지 포함하여 수 GB까지 되기도 함) 너무 많으면 적당히 제외시키도록 하자.
    • Resource Root: 말 그대로 Resource Root로 지정한다.
    • Template Folder: 템플릿이 있는 폴더에 지정하면 된다. Pure Python을 쓸 때에는 별 의미 없다.
  • Add to Favorites: Favorites창에 해당 디렉토리나 파일을 추가한다. 즐겨찾기 기능이랑 같다. 프로젝트 창 아래에서 창을 찾을 수 있고, Alt + 2 단축키로 토글할 수 있다.

새 파일 생성

이제 우클릭 > New > Python File로 새 파이썬 파일을 하나 생성하자. (현재 프로젝트 이름은 PythonTutorial이다)

안타깝게도 새 Python 파일 생성을 위한 단축키는 지정할 수 없는 듯하다.


코드 실행(전체 또는 선택)

그리고 원하는 파일명을 입력한다. 필자는 tutorial이라고 입력하겠다. 그러면 파일명은 tutorial.py가 될 것이다.

이제 코딩할 수 있는 창이 열렸으니 코드를 입력하자.

print('Hello Pycharm!')

코드를 작성했으면 실행을 해 보아야 하지 않겠는가? 실행하는 방법은 여러 가지가 있다.

  • 실행하고 싶은 코드 라인에 커서를 놓거나 실행할 만큼 드래그를 한 다음 에서 단축키를 바꿨다면 Ctrl + Enter, 바꾸지 않았다면 Alt + Shift + E를 누른다. 그러면 Python Console이라는 창이 아래쪽에 열리면서 실행한 코드와 실행 결과가 나타난다. 역시 단축키를 설정했다면 Alt + 8로 열 수 있다. PyCharm Default settings에는 단축키가 할당되어 있지 않다.
    • 이것은 정확히는 Interpreter라고 부르는 대화형 파이썬 창에서 실행시키는 것이다. 명령창(cmd, terminal)에서 python을 실행시키고 코드를 입력하는 것과 같은 형태이다. Jupyter notebook과도 비슷하다.
    • 장점은 명령창에서 바로 입력하는 경우 오타가 나면 다시 입력해야 하는데 편집기에 코드를 써 놓고 필요한 만큼만 Ctrl + Enter로 실행시키는 이 방식은 코드 수정과 재사용이 훨씬 편하다는 것이다.
    • 콘솔에 문제가 있거나 해서 현재 실행창을 재시작하고 싶으면 Python Console 왼쪽 Rerun 버튼(화살표)을 누르거나 Ctrl + F5를 입력한다.
    • 참고로 PyCharm 아래쪽/왼쪽/오른쪽에 있는 창들 중에서 옆의 숫자는 단축키를 간략하게 나타낸 것이다. 예를 들어 필자는 좀 전 설정에서 Python Console 창의 단축키를 Alt + 8로 설정해 놨는데, 그래서 옆에 8 이라는 숫자가 표시된다.
  • Run > Run…을 누르면 실행시키고 싶은 파일 목록이 나타난다. 이 중 원하는 파일(현재는 tutorial)을 선택하면 Terminal이라는 창에서 해당 파일의 전체 코드가 실행된다.
    • 다시 실행할 때는 Run > Run을 선택하면 마지막으로 실행한 파일이 전체 실행된다.
    • 아래 그림의 Terminal 창 왼쪽의 ReRun 버튼을 눌러도 마지막으로 실행한 파일이 다시 실행된다. 단축키는 Ctrl + F5이다.
    • PyCharm 오른쪽 위에서도 실행할 파일을 선택 후 실행시킬 수 있다.
  • PyCharm 아래쪽의 Terminal 창을 클릭하거나 Alt + T 단축키(바꾼 것이다)로 Terminal 창을 열어서 python tutorial.py를 입력한다.
    • 그렇다. Python 파일 실행 방법과 똑같다. 이 Terminal 창은 명령창(cmd 또는 터미널)과 똑같다.
    • 대략 tu 정도까지만 입력하고 Tab 키를 누르면 파일명이 자동완성된다.
    • 이 방법도 역시 해당 파일에 들어있는 모든 코드를 전체 실행시킨다.
    • 터미널 창 답게 여러 개의 세션을 열어 놓을 수 있다. 기본적으로 Local이라는 이름의 탭이 생성되며, 오른쪽의 + 버튼을 클릭하라.
  • Project 창에서도 해당 파일을 우클릭 > Run (파일명)을 클릭하면 해당 파일의 코드 전체가 실행된다.
  • 편집 창에서도 파일명 탭을 우클릭 > Run (파일명)해도 된다. 실행 방법은 많다.
  • Terminal에서 Local Environment에서 실행되는 대신, Remote SSH session에서 실행시키는 방법은 여기를 참고하면 된다.

고급 설정: working directory

working directory란 말 그대로 작업 중인 directory로, 작업 중인 환경의 path 또는 실행하려는 파일의 경로를 의미한다.

현재 실행하는 python 파일 또는 code가 working directory에서 진행되며, 상대 경로는 모두 이 working directory에서 출발한다.

PyCharm에서 코드를 실행하다 보면 경로가 자꾸 안 맞아서 오류가 나는 경우가 꽤 있는데, 이는 working directory가 잘 지정되지 않아서 생기는 문제이다.

현재 코드 혹은 파일의 working directory가 어딘지 알려면 다음 코드를 넣어서 실행해보자.

import os
print(os.getcwd())

이제 PyCharm에서 working directory를 설정하는 방법을 살펴보자.

PyCharm에는 기본적으로 3가지의 실행 방법이 존재한다.

  • Ctrl + Enter로 interpreter에서 실행
  • Terminal에서 python <filename>으로 실행 또는 python 명령어로 interpreter 실행
  • Run 메뉴로 실행

각각의 경우에 working directory를 설정하는 방법이 조금 다르다.

먼저, Ctrl + Enter로 interpreter에서 실행하는 경우를 보면:

Settings에서 working directory를 검색하자.

그리고 Build, Execution, Deployment > Console > Python Console 메뉴를 보면 Working directory를 설정하는 메뉴가 보인다. 이 경로를 바꿔준 후 Python Console을 재시작하면 working directory가 변경된다.

다음으로 Terminal에서 실행하는 경우는 간단하다. cd <path>명령어를 terminal에 입력하여 시작 경로를 바꿔준 다음 python <filename>이나 python을 실행하면 현재 위치가 working directory가 된다.

마지막으로, Run 메뉴로 실행할 때를 보면:

Ctrl + Shift + F5으로 run configuration을 생성하면, 기본적으로 해당 파일이 위치한 디렉토리가 working directory로 지정된다.

해당 configuration만 바꿀 생각이라면 중간의 Working directory를 원하는 path로 변경해주면 된다.

만약 지금 다루는 프로젝트 전체의 default working directory 설정을 하고 싶다면 위 그림의 왼쪽 아래에 있는 Edit configuration templates...를 클릭한다.

그 다음에 아래 그림처럼 Working directory 항목을 변경하면 된다. 프로젝트 파일에 넣으려면 오른쪽 위 박스의 Store as project file을 체크하자.


편집 창(코드 편집기)

코드를 편집하는 부분에도 여러 기능들이 숨어 있다.

위 그림의 오른쪽 부분을 보자. 경고인 듯한 느낌표와 함께 여러 색깔의 줄이 있다. 현재 커서는 9번째 라인의 example 변수에 위치해 있다.

  • 먼저 왼쪽에는 줄 번호(line number)라는 것을 다들 알 수 있을 것이다.
    • 하지만 이 단축키는 모르는 사람이 많다. Ctrl + G를 누르면 원하는 라인으로 이동할 수 있다. 줄의 어느 부분으로 이동할지도 line:column 형식으로 정할 수 있다. 줄 번호만 지정하고 싶으면 그냥 숫자만 입력하면 된다.
  • 빨간 화살표가 가리키고 있는 경고 표시는 현재 이 파일에 syntax error가 있다는 뜻이다. 메인 화면에도 해당 부분에는 빨간 줄이 그어진다(printf). 그리고 오른쪽에도 빨간색 bar가 생긴다.
    • 이 bar들은 현재 파일에서의 상대적 위치를 뜻한다. 즉, 예를 들어 맨 아래에 있는 오류 코드가 화면에 안 보이더라도 bar는 제일 아래쪽 근처에 표시된다.
  • 커서가 위치한 곳이 변수나 함수 등이라면 해당 파일의 모든 부분에서 같은 이름을 가진 변수(또는 함수)에는 옅은 초록색 배경색이 칠해진다. 그리고 해당 변수(함수)가 선언된 곳에는 옅은 주황색으로 배경색이 칠해진다(이 색깔은 Settings에서 바꿀 수 있다). 어디서 사용되고 있는지 쉽게 알 수 있다. 그리고 그림에서 오른쪽에도 주황색 또는 초록색 짧은 bar가 생긴 것을 볼 수 있다.
    • 옅어서 잘 안보인다면 색깔을 바꾸거나 아니면 Find and Replace(Ctrl + H)로 찾으면 더 선명하게 표시되기는 하는데, 해당 이름을 포함한 다른 변수 등도 같이 선택된다는 문제가 있다. 적당히 선택하자.
  • 특별히 TODO 주석문은 일반 회색 주석과는 다르게 연두색으로 눈에 띄게 칠해진다. 또한 오른쪽에 파란색 bar가 생긴다. 이 주석은 참고로 TODO 창(Alt + 6)에서도 확인 가능하다. 못다한 코딩이 있을 때 쓸 수 있는 좋은 습관이다.

편집 창의 아무 부분을 우클릭하여 Local History > Show History를 클릭하면 해당 파일이 어떻게 수정되어 왔었는지가 저장된다. 잘 안 쓸 수도 있지만 잘못 지운 상태로 코딩을 좀 진행했다거나 하는 상황에서 쓸모 있는 기능이다.


.ipynb 파일 사용

PyCharm에서도 .ipynb파일을 사용할 수 있다. 웹브라우저에서 보는 jupyter notebook과 모양이 매우 흡사하다.

위쪽의 셀 실행 버튼을 누르면(초록색 삼각형) jupyter 서버 주소 토큰을 입력하라고 나온다. 본인이 jupyter 서버를 실행시켰다면 jupyter notebook 서버를 켠 상태에서 해당 주소를 입력해주고 실행하면 .ipynb 파일을 브라우저에서 쓰는 것처럼 사용할 수 있다.


자동완성 기능

일반적인 편집기에는 다 들어있는, 변수나 함수 등의 이름을 일부만 입력하고 Tab 키를 누르면 자동완성이 된다는 것은 알고 있을 것이다.

아래는 일부 코드 블록을 간편하게 입력할 수 있는 방법을 소개한 것이다.

  • 클래스 내부의 함수를 작성할 때는 (를 입력하는 순간 self 인자가 자동으로 추가된다. 기본적으로 써야 하는 인자이기 때문에 자동 추가되며, 이를 비활성화하고 싶으면 File > Settings > Editor > General > Smart Keys에서 바꿀 수 있다.
  • 함수나 클래스를 작성할 때, 삼중큰따옴표를 함수 prototype 정의 바로 밑에 써 주면 깔끔하게 함수 사용법을 정리할 수 있는 주석이 나타난다.
    • 빈 줄에 함수 설명을, param에는 각 인자의 설명을, return에는 이 함수의 반환값에 대한 설명을 써 주자.

빠른 선택, 코드 정리, 편집 등등 단축키

원하는 부분을 빠르게 선택할 수 있는 단축키는 많다. 이를 다 알고 빠르게 할 수 있다면 코딩 속도는 아주 빨라진다.

  • 변수/함수 더블클릭: 해당 변수 이름 선택
  • Ctrl + Z: 실행 취소(Undo)
  • Ctrl + Shift + Z: 재실행(Redo)
  • Ctrl + D(Duplicate): 현재 커서가 있는 한 줄(또는 드래그한 선택 범위)을 복사해 아래에 붙여 넣는다.
  • Ctrl + X / Ctrl + C: 현재 커서가 있는 한 줄(또는 드래그한 선택 범위)을 잘라내기/복사한다. 한 줄도 된다는 것을 기억하라.
  • Ctrl + W: 현재 선택 범위의 한 단계 위 범위를 전체 선택한다. 무슨 말인지 모르겠다면 직접 해 보면 된다. 범위는 블록이나 괄호 등을 포함한다.
  • Tab: 현재 커서가 있는 한 줄(또는 드래그한 선택 범위)를 한 단계(오른쪽으로 이동) indent한다.
  • Shift + Tab: 현재 커서가 있는 한 줄(또는 드래그한 선택 범위)를 반대 방향으로(왼쪽으로 이동) 한 단계 indent한다.
  • Ctrl + A: 현재 파일의 코드를 전체선택한다.

  • Ctrl + Shift + O(Import Optimization): 코드 내에 어지럽게 널려 있는 import들을 파일 맨 위로 모아 잘 정리한다.
  • Ctrl + Shift + L: 코드의 빈 줄, indentation 등을 한 번에 정리한다.

  • Ctrl + 좌클릭: 해당 변수/함수가 선언된 위치로 화면/커서가 이동한다. 변수가 어떻게 정의됐는지 또는 함수가 어떻게 생겼는지 보기 유용하다.
  • Alt + 좌클릭: 커서를 원하는 곳에 일단 놓고, 또 같은 것을 입력하고 싶은 곳에 Alt를 누른 채로 새로 클릭하면, 커서가 여러 개가 되는 것을 확인할 수 있다. 이 상태에서 키보드로 입력을 시작하면 여러 곳에서 한번에 입력이 가능하다.

이외에도 기능은 정말 많다(Toggle Case, Convert indents to space/tab, Copy as Plain Text, Paste without Formatting, …). 한번 잘 찾아보자.

각각의 기능들은 Edit 탭이나 Navigate, Code, Refactor 탭 등에 잘 분류되어 있다. 한번쯤 살펴보고 본인에게 필요한 기능들은 기억해두면 좋다.


찾기(및 바꾸기), (Ctrl + F | Ctrl + H)

찾기 및 바꾸기의 기본 단축키는 Ctrl + R이다(Replace). 많은 다른 프로그램들은 Ctrl + H를 쓰기 때문에 바꾸는 것도 좋다.

여기도 여러 기능들이 있다. 찾기 설명은 찾기 및 바꾸기의 설명 안에 포함되므로 생략하겠다.
아래에서 설명할 기능들은 모두 그림에 나온 버튼이나 체크박스 등에 대한 것이다.

  • 왼쪽 검색창에 찾고자 하는(또는 대체될) 문자열 또는 정규식을 입력한다. 아래쪽 창에는 대체할 문자열을 입력한다.
    • 왼쪽 돋보기를 클릭하면 이전에 검색했던 문자열들을 재검색할 수 있다.
  • F3: 다음 것 찾기
  • Shift + F3: 이전 것 찾기
  • Find All: 전부 다 찾아서 보여준다.
  • Select All Occurrences: 매칭되는 결과를 전부 선택한다.
  • Show Filter Popup: 찾을 범위를 지정할 수 있다. 전부(Anywhere), 주석에서만(In comments), 문자열에서만(In String Literals), 둘 다에서만, 혹은 제외하고 등의 필터를 설정 가능하다.
  • Match Case: 체크하면 대소문자를 구분한다.
  • Words: 정확히 단어로 맞아야 할 때(해당 문자열을 포함하는 단어를 제외하는 등) 체크한다.
  • Regex: 정규표현식을 사용하여 찾는다. 잘 쓸 줄 안다면 아주 좋다.
  • 오른쪽에는 몇 개나 매칭되는 문자열을 찾았는지 보여준다(3 matches). 만약 하나도 없으면 문자 입력 창이 빨갛게 되면서 No matches라고 뜬다.
  • Replace(Alt + p): 현재 선택된 부분을 대체한다..
  • Replace all(Alt + a): 매칭되는 모든 문자열을 찾아 대체한다.
  • Exclude: 해당 매칭된 부분은 대체할 부분에서 제외한다.
  • Preserve Case: 대체 시 대소문자 형식을 보존한다.
  • In Selection: 파일 전체가 아닌 선택한 부분에서만 찾는다.

더 넓은 범위에서 찾기

선택한 파일 말고 더 넓은 범위에서 찾으려면 Ctrl + Shift + F를 누르거나 다음 그림을 참고한다.

위의 Match Case등은 사용법이 똑같지만, 여기서는 파일뿐 아니라 프로젝트 전체, 모듈, 디렉토리, 또는 특정 범위(scope)에서 찾을 수 있다. Edit > Find > 안의 다른 선택지들 역시 사용법은 크게 다르지 않으니 참고하자.

다른 (범용) 찾기 단축키로 Shift + Shift(Shift 키를 두번 누름)이 있다. 한번 해 보자.

변수/함수 등이 사용된 위치 찾기

찾고자 하는 변수/함수를 우클릭하여 Find Usages를 클릭하거나 Alt + F7을 누른다.

그러면 해당 변수/함수가 어디서 사용되었는지 정보가 전부 나온다. 왼쪽에 있는 많은 버튼들로 적절한 그룹별로 묶거나 하는 등의 작업을 할 수 있다.

Refactor(이름 재지정)

변수명을 바꾸고 싶어졌을 때가 있다. 무식하게 일일이 바꾸거나, 아니면 Find and Replace로 선택적으로 할 수도 있다.

하지만 매우 쉽고 편리한 방법이 있다. 해당 변수를 선택하고 Shift + F6을 누른다.

원하는 이름으로 바꾸고 Refactor을 누르면 해당 변수만 정확하게 원하는 이름으로 바뀐다. 심지어 import해서 사용한 다른 파일에서도 바뀐다.

아주 편리하다.

Diff(Differences viewer for files)

2개의 파일 코드가 어떤 차이가 있는지 알아보려면, Project 창(Alt + 1)에서 2개의 파일을 Ctrl키로 동시 선택하거나, 한 파일을 누르고 Ctrl + D를 누른다. 그러면 1개의 파일을 선택했다면 추가 파일을 선택하는 창이 나오고, 이것까지 선택하면 2개의 파일을 비교하는 창이 나온다.

  • 파란색으로 표시된 부분은 2개의 파일이 다른 코드,
  • 회색으로 표시된 부분은 1번째 파일(왼쪽 파일)에만 있는 코드,
  • 초록색으로 표시된 부분은 2번째 파일(오른쪽 파일)에만 있는 코드

를 나타낸다.

중간의 >><< 표시를 누르면 해당 코드가 화살표 방향으로 복사되어 덮어씌운다.


Bookmark(북마크) 기능


창 위치 및 크기 변경

Python Console 등의 창은 위치나 크기가 변경 가능하다. 크기는 창의 경계에 마우스 커서를 갖다대는 방식이니 굳이 설명하지 않겠다.
위치는 탭을 끌어서 이동시키거나 아니면 우클릭 > Move to > 원하는 곳을 선택하면 된다.

또 모니터를 2개 이상 쓴다면 View Mode에서 해당 설정을 변경할 수 있다. 기본은 PyCharm 내부에 위치 고정된 Dock Pinned 모드이다. Float이나 Window를 선택하면 위치를 자유롭게 이동할 수 있다.

모니터 크기는 충분한데 코드는 위아래로만 길게 보여서 공간이 아까웠다면, PyCharm에서는 굳이 그럴 필요 없다. Vim의 Split View와 비슷한 기능이 있다.

편집 창(메인 화면)의 탭을 우클릭한 다음 Split Vertically를 클릭해 보라. Split Horizontally도 괜찮다.

동일한 파일을 여러 번 열고 다른 부분을 보는 것도 가능하다. 꽤 유용한 기능이다.


Favorites 창(Alt + 2)

Alt + 2를 눌러 Favorites 창을 연다.

말 그대로 즐겨찾기이다. 자주 사용하는 파일을 Favorites에 등록할 수 있다. 기본적으로 현재 프로젝트 이름으로 리스트가 하나 생성되어 있다.
이게 싫거나 새로운 리스트를 추가하고 싶으면 아래 그림의 오른쪽에 보이는 Add to New Favorites List를 클릭하라.

그러면 Favorites 창에 해당 리스트에 추가한 파일이 등록된다. 이제 프로젝트 창에서 찾을 필요 없이 바로 파일을 열어볼 수 있다.


Run 창(Alt + 4)

Alt + 3은 기본적으로 할당되어 있지 않다. 추가하고 싶으면 추가하라.

Run 창은 조금 전 코드를 실행할 때 본 것이다. 여기서는 왼쪽에 몇 가지 버튼이 있는데, 각각

  • Rerun(마지막 실행 파일 재실행)
  • Stop(현재 실행 중인 파일 실행 중단)
  • Restore Layout(레이아웃 초기화)
  • Pin Tab(현재 실행 탭 고정)
  • Up/Down to Stack Trace(trace 상에서 상위 또는 하위 단계로 이동)
  • Soft-Wrap(토글 키. 활성화 시 출력 내용이 한 줄을 넘기면 아래 줄에 출력됨. 비활성화 시 스크롤해야 나머지 내용이 보인다)
  • Scroll to the end(제일 아래쪽으로 스크롤)
  • Print(출력 결과를 정말 프린터에서 뽑는 거다)
  • Clear All(현재 출력 결과를 모두 지우기)

Soft-wrap 등은 꽤 유용하므로 잘 사용하자.

Run 창을 우클릭 시 Compare with Clipboard 항목이 있는데, 현재 클립보드에 있는(즉, Ctrl + C 등으로 복사한) 내용과 출력 결과를 비교하는 창을 띄운다. 정답 출력 결과를 복사해 놨다면 유용하게 쓸 수 있다.


TODO 창(Alt + 6)

PyCharm에서는 주석(#)을 달면서 앞에 TODO:라는 문구를 적으면 해당 주석은 특별히 눈에 띄는 연두색으로 바뀐다.

TODO들은 앞으로 해야 할 것을 모아 놓은 것이다. 이를 나중에 찾아보려면 PyCharm 아래쪽의 TODO 창을 클릭하거나 Alt + 6으로 열자.
그럼 현재 프로젝트의 어느 부분이 미완성인 채로 남아 있는지 한번에 볼 수 있다. 기본적으로 파일별로 정렬되어 있다.


Structure 창(Alt + 7)

현재 파일이 어떤 구조로(클래스는 무엇을 포함하고, 함수는 어떤 statement들을 포함하는지 등) 되어 있는지 살펴보려면 코드를 한줄한줄 다 뜯어보는 대신 Structure 창에서 볼 수 있다.
어떤 변수가 어디에 정의되었는지까지도 볼 수 있다.


Python Console 창(Alt + 8)

단축키는 필자가 지정한 것이다.

이는 명령창에서 Python을 실행했을 때 나타나는 것과 같다고 말했었다. 어려울 것 없이 똑같이 사용할 수 있다.

단, 사용 환경에 따라 이런 대화형 창에서는 가용 메모리를 다 쓰지 못하는 경우가 있다. 예를 들어 GPU 메모리를 수 GB씩 쓰는 학습 알고리즘 등의 경우 터미널에서 python 파일명으로 실행하면 잘 작동하는데 대화형 창에서 실행하면 작동이 중지되는 것을 종종 볼 수 있다. 참고하자.


Version Control 창(Alt + 9)

이건 Git 기능을 PyCharm에 옮겨놓은 것과 같다. git 사용법을 안다면 쉽게 이용 가능하다. 하지만 git을 잘 알고 있다면 그냥 Python의 terminal 창을 열어서 git 명령어를 치는 것이 편할 수 있다.


SciView 창(Alt + 0)

PyCharm에서 matplotlib 등으로 그래프를 그린다면 바로 이 창에 표시가 된다.

여기서 한 장씩 보기 또는 grid 모드로 보기 등을 선택할 수 있고, 확대 및 축소, 1:1, 창 크기에 맞추기 등의 옵션도 가능하다.

그림 오른쪽 위에 작게 표시되는 x 표시를 누르면 그림을 지울 수 있다. 또한 우클릭을 통해 저장하거나 전체 삭제 등의 작업을 할 수 있다. 배경화면 사진으로도 지정할 수 있다(!).

참고로 pycharm에서는 일반적으로 plt.show()를 그냥 하면 그림이 표시되지 않는 경우가 있다. 이에 대한 해결법은 링크를 참고하자.

plt.interactive(False)
# plt.show(block=True)

디버깅(Debugging)

PyCharm의 훌륭한 기능 중 하나이다. 사실 웬만한 코드 편집기에 있기는 한데, python을 쓰는 사람들 중에 이를 활용할 줄 알아서 쓰는 경우는 생각보다 많지 않은 것 같다.
(물론 알아서 코드를 수정해 주는 것은 아니다…)

예를 들어 다음과 같은 프로그램을 짰다고 생각해 보자.

def func(idx):
    example[idx] = example[idx - 2] + example[idx - 1]

example = [1] * 20

for i in range(20):
    func(i)
        
for i, e in enumerate(example):
    print('fibonacci({})\t: {:8d}'.format(i, e))

결과는 다음과 갈다.

fibonacci(0)	:        2
fibonacci(1)	:        3
fibonacci(2)	:        5
fibonacci(3)	:        8
...

피보나치 수열은 2부터 시작하지 않으므로 잘못되었다. 그러면? 디버깅을 시작한다(물론 간단한 예시라서 바로 고칠 수 있지만 우선 넘어간다).

디버깅을 시작하는 방법은 여러 가지가 있다. 이는 코드를 실행할 때와 매우 비슷한데, 초록색 삼각형 대신 벌레 모양의 아이콘을 클릭하면 된다. 그게 다이다. Run 대신에 Debug를 누를 뿐이다.

그러나 시작하기 전 Breakpoint를 하나 설정한다. 버그가 있다고 생각하는 시점 직전에 설정하면 된다. 우선 이번 예시에서는 example을 선언한 라인에 설정하겠다. 코드 왼쪽, 라인 번호 바로 오른쪽 빈 공간을 클릭하자.

그러면 빨간 점과 함께 해당 라인의 배경이 빨간색으로 칠해진다.

그리고 Run > Debug를 클릭한다. 벌레를 클릭해도 좋다.
뭔가 다른 프로그램이 실행되고 있는 것 같으면, 실행하려는 파일명을 다시 확인하라.

디버깅할 때는 코드를 그냥 실행할 때와는 동작이 많이 다르다.

  • 실행 시에는 코드가 처음부터 끝까지 멈춤 없이 진행된다.
    • 물론 사용자의 입력을 기다리거나, 계산이 오래 걸리거나, sleep()등으로 지연시키는 경우는 예외이다. 그러나 이 경우에도 입력/계산/시간이 완료되면 자동으로 다음 코드를 지체 없이 빠르게 실행한다.
  • 디버깅 시, 처음에는 Breakpoints 까지는 실행 시와 똑같이 순식간에 진행된다. 그러나 Breakpoints에 도달하면 그 라인의 실행 직전까지만 코드가 실행된 후 대기 상태로 전환한다(이름이 왜 breakpoint이겠는가?).
  • 그리고 이후 진행은 사용자가 무엇을 클릭했느냐에 따라 달라진다. 디버깅 모드에서는
    • 한 줄 실행(딱 한줄만 실행),
    • 지정 위치까지 실행(해당 지점을 Breakpoints 삼아 그 라인 직전까지 실행),
    • 어떤 함수 내부로 들어가 한 줄씩 실행,
    • 현재 실행 중인 함수 밖으로 나오는 데까지 실행
    • 등등의 옵션이 있는데, 각각의 옵션에 따라 딱 필요한 만큼까지만 코드가 실행된 후 대기 상태로 멈춰 있는다.

우선 F8을 눌러보자. Step Over이라고 되어 있다. 위 그림에서 빨간 박스 안의 첫 번째 아이콘을 클릭해도 된다.

  • 그럼 한 줄을 실행하고 다음 statement로 넘어가 있다. 빈 줄은 건너뛴다.
  • 실행한 줄 옆에 그 줄의 변수에 들어 있는 값이 업데이트된다. example 변수는 list 타입이며, 값 1을 20개 갖고 있는 리스트이다.
  • 왼쪽 아래 untitled.py: 7로 값이 바뀌었다. 현재 untitled.py의 7번째 줄을 실행하기 직전이란 뜻이다.
  • 아래쪽 Variables 창에서 접근할 수 있는 변수 목록이 업데이트되었다. 현재는 example 하나뿐이므로 그 값을 볼 수 있다.

Variables 창에서는 현재 scope에서 접근가능한 변수 목록이 자동으로 업데이트되지만, 미리 보고 싶거나 혹은 계산 결과 등을 보고 싶다면 새로운 Watch를 추가할 수 있다. Variables 창 아무 곳이나 우클릭하면 새로 보고 싶은 변수 혹은 수식 결과값 등을 추가할 수 있다. 예시로 example * 2를 추가해 보았다.

Step Over 외에 다른 버튼들은 다음과 같다.

  • Step Into(F7): 코드가 어떤 함수를 실행하는 경우(예: 예시의 func, 내장 함수인 print 등), 해당 함수의 내부로 들어가서 실행할 수 있게 해 준다.
    • 아래 예시는 func() 내부로 들어간 모습을 보여준다.
    • 참고로 argument로 무엇이 전달되었는지 등도 표시된다(아래 그림의 경우 idx: 5라고 되어 있는 것을 볼 수 있다). argument뿐 아니라 업데이트되고 있는 변수들 모두 값을 보여주며, 방금 업데이트된(조금 전 실행한 라인의 결과) 값은 회색이 아닌 주황색으로 표시된다.
  • Step Into My Code: 위의 Step Intoprint 같은 내장 함수들 안으로까지 파고들어 코드를 실행한다. 내장 함수가 오작동하는 것은 아니기 때문에 자신의 코드만 검사하고 싶다면 이쪽을 택하자.
  • Force Step Into: 말 그대로 강제로 함수 안으로 들어가 실행시킨다. 비활성화된 경우가 많을 것이다.
  • Step Out(Shift + F8): 실행되고 있는 함수 밖으로 나오는 데까지 실행시킨다. func 또는 print 끝난 다음 줄로 이동한다.
  • Run to Cursor(Alt + F9): 커서가 있는 곳으로까지 코드를 실행시킨다. 반복문 내부인 경우 가장 가까운 반복 단계에서 멈춘다.

위의 모든 명령은 Breakpoint에서 걸린다. 즉, Run to Cursor 등으로 많이 이동하려 해도 그 사이에 Breakpoint가 있으면 그 직전까지만 실행된 상태로 멈춘다.

이런 기능들을 활용하면서 디버깅하면 어느 단계에서 코드가 잘못 되었는지 확인할 수 있다.

Breakpoint에는 한 가지 중요한 기능이 있다. 지금까지 설정한 것은 코드가 설정된 라인에 가면 무조건 멈추는데, 이 조건을 바꿀 수 있다. 8번째 줄에 Breakpoint를 설정하고, Breakpoint를 나타내는 빨간 원을 우클릭하자.

조건을 설정할 수 있는 창이 나온다.

아래의 More...를 클릭하면,

더 자세한 조건을 설정할 수 있다.

예시를 한 개만 들어보겠다. 8번째 줄의 Breakpoint를 아래처럼 설정한다. i == 5 일 때만 Breakpoint가 작동할 것이다.

조건을 설정하면 빨간 원 옆에 ?가 생기면서 condition이 설정되었음을 나타낸다.
디버깅 모드를 종료했다가 다시 시작한 다음, 프로그램 끝에 커서를 놓고 Run to Cursor를 실행해 보자.

그러면 i == 5일 때 func 함수 내부에 멈춰 있음을 볼 수 있다. 한 번 더 Run to Cursor로 이동하면 그때서야 끝부분으로 이동한다. 즉 i == 5인 조건을 지났기 때문에 다시 발동하지 않는 것이다.

이 기능은 반복문이 여러 차례 반복된 뒤에야(예: 1000번쯤, Step Over를 1000번씩 누르긴 싫을 것이다) 버그가 나타나는 경우 해당 지점 직전에까지 가도록 Breakpoint를 설정하는 방법으로 쉽게 탐색할 수 있다.

잘 쓰면 꽤 유용하니 이것도 익혀 두도록 하자.


Profilers(코드 실행시간 측정)

코드 실행시간을 측정할 때 매번 코드 시작과 끝 지점에 start_timeend_time 같은 코드를 삽입하지 않고도 특정 함수나 코드 일부분 등의 실행 시간을 측정하는 기능을 PyCharm에서 제공한다. 이는 Terminal이나 IPython 등으로 실행한 것이 아닌 PyCharm의 Run 과 같은 방식으로 파일을 실행시켰을 때(Configurations에서 설정 가능) 설정 가능하며, Professional 버전에서만 이용 가능하다.

정확히는, Configurations에서 실행할 파일을 지정한 다음, Run 버튼이 아닌 Profile 버튼을 클릭한다.

temp.py 파일을 다음과 같이 작성했다고 하자.

이제 이 코드에서 어느 부분이 실행시간의 많은 부분을 차지하는지 알아보자. PyCharm의 우상단에 있는 Profile 버튼을 클릭한다.

그러면 실행창에는 다음과 같이 Starting cProfile profiler라는 문구가 출력되면서 실행이 된다. done 출력 이후에 한 줄이 더 출력되어 있는데, Snapshot(pstat 파일)이 지정된 경로에 저장되었다는 뜻이다.

그리고 실행이 끝나면 확장자가 pstat인 파일이 열린다. 실행이 너무 오래 걸린다면, 위 그림의 빨간 박스(Capture Snapshot, 실행이 끝난 상태에서는 비활성화됨)를 클릭하면 snapshot이 바로 저장되면서 중간 결과를 볼 수 있다.

pstat 파일에는 2개의 탭이 있다. Statistics 탭에는 각 함수별로

  • 실행 수
  • 해당 함수에 포함된 모든 함수의 실행 시간을 모두 더한 값
  • 해당 함수 자체만의 실행 시간의 총합(해당 함수가 여러 번 실행되었을 수 있으므로) 이 나열되어 있다.

정확히는 사용자 정의 함수와 같은 일반 함수 외에도 Python 내장 함수(print 등) 및 기본(__init__ 등) 함수, class, 실행 파일 등이 포함된다.

원하는 함수명을 오른쪽 클릭하면 해당 함수가 위치한 곳으로 이동하거나, 아래의 Call Graph에서 찾아볼 수도 있다.

Call Graph 탭에서는 어떤 파일/함수에서 어떤 함수가 실행(call)되었고, 각 함수의 실행 시간을 전부 볼 수 있다.
이 기능은 복잡한 코드가 어떤 과정으로 실행되는지를 대략 알아보는 데도 쓸 수도 있다(순서는 Traceback을 보는 것이 낫다).

왼쪽 위에 있는 +, - 등의 메뉴에서는 그림 확대/축소, 화면 맞추기, 이미지로 저장 등을 수행할 수 있다.


Configurations(실행 시 parameter 설정)

실행(Run)이나, 디버깅(Debugging) 버튼을 통해서 실행하고자 할 때, 엉뚱한 파일이 실행되는 경우가 있다. 이는 실행 버튼 바로 옆의 실행 파일명 또는 configuration 이름을 살펴보고 원하는 부분이 아니라면 바꿔주도록 하자.

참고로, Run/Debug configuration 설정 창에서는 실행 파일뿐 아니라 인자(argparse 등에서 사용하는 argument)를 설정해 줄 수도 있다. Python에서는 기본적으로 실행 시 인자를 주기 위해서는 명령창에서 python <실행할 파일.py> --option1 <option1> 형식으로 실행시켜야 하는데, PyCharm에서는 이 설정을 저장해두고 바로바로 쓸 수 있다. 위의 그림에서 Edit Configurations를 눌러보자.

순서대로 설명하면,

  • (빨간색) configuration의 이름을 설정할 수 있다. 기본적으로 실행하고자 하는 파일명으로 설정되며, 파일명으로 뿐만 아니라 원하는 이름으로 변경할 수 있다.
  • (주황색) 실행하고자 하는 python 파일을 설정할 수 있다. 여기서 직접 추가하거나, 상단 메뉴 바의 Run에서 새로 파일을 설정하면 추가된다.
  • (노란색) 딥러닝 등에서 보통 많이 쓰는 argparse에서 인자를 받곤 하는데 이를 여기서 추가할 수 있다. 물론 argparse 뿐만 아니라 sys.argv[]가 받는 것도 동일하다. 사실 이게 제일 중요한 듯
  • (초록색) 원하는 실행 환경을 바꿔줄 수 있다.
  • (파란색) 실행 폴더의 위치를 지정한다. 기본적으로 실행 파일과 같은 위치로 지정되며, Python 코드 내의 상대 경로는 이 경로의 영향을 받는다.
  • (남색) 콘솔에서 실행시킬지 등을 결정할 수 있다. 기본적으로는 해제되어 있다.
  • (보라색) 실행시키기 전에 tool window 등을 미리 활성화 할 수 있다. 가능한 메뉴는 다음과 같다.

SSH를 통한 외부 서버 원격 접속

보통 ssh를 통해서 외부 서버에 진입할 때는 명령창에서 vim이나, 혹은 기타 조잡한(?) 편집기를 통해서 코드 수정을 하게 된다. 그러나, PyCharm Pro 버전은 SSH로 접속할 수 있는 외부 서버를 연결하여 코드를 편집하면서, 서버에 변경사항을 실시간으로 업데이트할 수 있다.

이 강력한 기능은 아쉽게도 community 버전에서는 지원하지 않는다.

먼저 새 프로젝트 또는 로컬에 존재하는 기존 프로젝트를 연다.
Settings > Project: name > Project Interpreter 로 이동한 뒤, 오른쪽 위의 톱니바퀴를 누르면 Add 또는 Show All이 뜬다. Add를 누르자. Show All을 누른 다음 + 버튼을 눌러도 좋다.

새 Interpreter를 만드는 과정에서, Virtualenv나 conda 등이 아닌 SSH Interpreter를 선택해준다. 그리고 서버 설정을 똑같이 입력해준다.

다음 화면에서 비밀번호도 잘 입력해준다.

그러면 이제 서버에 저장되어 있을 Interpreter를 설정하는 단계이다. 여러분이 그냥 Python 하나만 깔아놓고 쓰거나, Conda를 쓰거나, Virtualenv를 쓰거나 하는 경우마다 Python Interpreter의 위치는 전부 다르다.
아무튼 어딘가에 있을 python.exe를 잘 찾아서 경로를 지정해 주어야 한다. Ubuntu 환경에서 Miniconda를 쓰는 필자는 대략 다음과 같은 interpreter 경로를 갖는다.

Interpreter 경로 지정은 오른쪽의 디렉토리 아이콘을 누르면 서버에 존재하는 파일과 디렉토리를 볼 수 있다.
그리고 관리자 권한으로 실행해야 하는 경우가 있다면, 위 그림에서 파란색으로 표시한 Execute code using this interprete with root privileges via sudo 옵션을 체크한다. (보안 상 문제가 없으면 하는 거 추천)

다음으로는 아래쪽에 있는, 원격 서버의 파일과 로컬 파일을 동기화시키는 항목이 나온다. 이 부분의 의미는,

  • PyCharm의 기능은
    • 원격 서버를 ssh를 통해 vim 등의 편집기로 수정만 하는 방식이 아니라,
    • 로컬에 같은 파일을 복사한 채로 진행되며,
  • 로컬 파일을 수정하면 자동으로 원격 서버의 파일도 동기화가 되며(옵션을 체크했을 경우)
  • 로컬에서 실행 명령을 내리면 로컬에서 실행되는 것이 아닌 원격 서버에서 실행이 된다.

이를 위해서는 Sync folders 옵션의 오른쪽에 있는 디렉토리 아이콘(위쪽 빨간 박스)을 클릭한다. 그리고 Edit Sync Folders 대화창이 뜨면 동기화를 시킬 노란 박스로 표시한 Local PathRemote Path를 잘 지정한다(아래쪽 빨간 박스를 누르면 수정 가능). 클라우드 드라이브 서비스처럼 알아서 동기화가 된다.

초록 박스로 표시한 + 버튼을 누르면 동기화할 Path를 추가 지정할 수 있다. 이는 같은 Interpreter를 사용하는 여러 프로젝트가 있을 때 사용하면 된다.

원격 서버에 이미 파일이 존재하는 경우, 위 그림에서 파란 박스로 표시한 부분을 체크 해제한다. 반대로 로컬에서 처음 시작하는 경우, 체크해도 좋다. 만약 서버에 파일이 있는데 로컬 파일을 원격으로 자동 업데이트하는 옵션을 체크하면 원격 서버의 파일이 지워진다는 경고창을 보게 된다.

OK를 누른 뒤 Finish 버튼을 누르면 한번 더 비밀번호를 입력하는 창이 뜬다.

그러면 Interpreter 목록에서 원격 Interpreter를 확인할 수 있다.

</br>

다음으로 Settings > Build, Execution, Deployment > Deployment으로 이동한다. 그러면 Deployment에 조금 전 추가한 정보가 들어가 있을 것이다. 만약 없으면 아래 그림처럼 새 SFTP 서버를 추가한다.

그러면 서버 이름을 입력하는 대화창이 나온다. 입력해주자.

다음 그림을 보자.

  • Host에는 서버의 IP 주소(ex. 123.124.125.126),
  • 포트 번호는 원격 서버에서 허용한 번호,
  • User name은 서버의 사용자 이름,
  • 인증 방식은 보통 비밀번호를 많이 쓸 테니 사용자 이름에 맞는 비밀번호를 입력해준다. 비밀번호는 저장해도 좋다.
  • 그리고 아래쪽 Test Connection을 누르면 연결이 정상적인지 확인한다. 안 된다면 잘못 입력했거나, 외부 접속 또는 포트 등이 차단되어 있을 가능성이 높다. 테스트를 해보면 아래와 같은 창이 뜨는데, Yes를 눌러준다.
  • 정상적이면 연결이 성공했다는 메시지가 뜬다.
  • Root Path는 기본값으로 두어도 되고, 인증이 잘 되었다면 AutoDetect를 사용해도 된다. 특정 directory에서 시작하고 싶으면 오른쪽 디렉토리 아이콘을 눌러 직접 지정해준다.
  • Web Server URL와 그 아래 고급 옵션은 필수는 아니다.

그리고 Mappings 탭을 클릭하면 Local PathDeployment Path(Remote Path)를 mapping할 수 있는 탭이 나온다. 역시 디렉토리 아이콘을 눌러 경로를 지정해 준다. 이때 경로는 위에서 기억한 Root path에 더한 상대 경로임을 유의한다. 즉 mapping되는 Remote Path는 Root path + Deployment Path이다.

이제 Project: name > Project Interpreter에서 조금 전에 만든 Interpreter를 선택하고 설정을 마치면 파일 전송이 이루어진다.

코드 수정을 하면 자동 업로드가 된다(옵션을 체크했다면). 또한, 실행을 시키면 원격 서버에서 실행되게 된다.

참고. 원격 서버에서 실행을 하긴 하지만 linux 시스템에서 사용하는 bash 파일을 윈도우에서 실행시킬 수는 없다. 이 부분은 조금 아쉬운 부분이다.

로컬 -> 원격 또는 원격 -> 로컬 간 파일 전송을 수동/자동으로 할 수도 있다. Tools > Deployment를 누른다.

  • Upload to를 눌러 서버를 선택하면 현재 로컬 프로젝트 파일들을 저장된 원격 서버에 업로드할 수 있다.
  • Download from을 눌러 서버를 선택하면 마찬가지로 원격 서버의 파일을 로컬에 내려받을 수 있다.
  • Configuration을 누르면 조금 전 보았던 Mappings 탭을 포함해 설정을 다시 할 수 있다.
  • Automatic Upload를 누르면 토글이 되며, 로컬 파일을 원격 서버에 자동으로 업데이트할지를 결정할 수 있다.

Terminal에서 SSH session으로 열기

메뉴 바에서 Tools > Start SSH session...을 클릭하면 Select host to connect 창이 뜬다. 이때 아래쪽 목록에는 현재 프로젝트에 설정되어 있는 python environment들이 뜬다. SSH 연결을 추가하고 싶으면, Edit credentials...를 클릭한다.

그러면 위 그림과 같이 SSH Session 대화창이 뜬다. 여기서 보통 ssh 연결할 때처럼 서버 주소, 사용자명, 비밀번호 등을 입력하고 OK를 누르면 Terminal 창에서 로컬 환경 대신 SSH 환경에서 열리게 된다. 파일 접속은 물론이고 실행까지 원격 ssh 서버 상에서 이루어지게 된다.


오류 해결법

파이썬 가상환경 보안 에러(오류): about_Execution_Policies PSSecurityException

윈도우에서 powershell을 관리자 권한으로 실행한다.

Set-ExecutionPolicy Unrestricted을 입력한 다음에 Y(대문자)를 누르고 enter 입력하면 해결 완료된다.


References

공식 홈페이지에서 더 자세한 사용법을 찾아볼 수 있다.

Comment  Read more

Miniconda(Anaconda) 사용법(conda 설치 및 사용법)

|

Anaconda는 Continuum Analytics라는 곳에서 만든 파이썬 배포판으로 수백 개의 파이썬 패키지를 포함하는 환경을 구성한다. Anaconda로는 virtualenv와 같은 여러 개의 가상환경을 만들어 각각의 환경을 따로 관리할 수 있다.
그 중 Miniconda는 이것저것 설치할 것이 많은 Anaconda에서 패키지를 다르게 설치할 여러 환경들을 관리한다는 최소한의 기능만 가진 부분만 포함하는 mini 버전이다. 따라서 이 글에서는 Miniconda를 설치하여 가상환경을 관리하는 법을 알아보겠다.


설치

Anaconda를 설치하거나, Miniconda를 설치한다. 설치하고 싶을 운영체제와 버전에 맞는 것을 골라 설치한다. 설치 방법은 공식 홈페이지에 따로 설명되어 있다.

01_install Not recommended라고 되어 있는 옵션이지만 체크하면 PATH에 등록하지 않아도 된다(이건 환경마다 조금 다르다). 02_install

설치 후 다음 명령을 명령창(cmd / 터미널)에 입력해본다.

conda list

만약 다음과 같이 오류가 뜬다면 conda가 System PATH에 등록되지 않은 것이므로 등록을 해 준다.

03_install 04_install

윈도우10, Miniconda3인 경우 C:\ProgramData\Miniconda3\Scripts를 PATH에 등록해 준다.
경우에 따라 등록이 안 될 수도 있는데, 다음도 추가해 주면 확실히 잘 된다.

  • C:\ProgramData\Miniconda3\Library
  • C:\ProgramData\Miniconda3\

설치 경로를 기본 경로에서 바꿨으면 당연히 Miniconda 앞 경로를 바꿔줘야 한다.

설치 패키지 목록은 다를 것이지만 다음과 같이 뜬다. conda list는 현재 환경(기본 환경의 이름은 base이다)에서 설치된 패키지 목록을 나타내는 명령이다.

05_conda_list


가상환경 목록 확인, 생성 및 삭제

다음을 명령창에 입력한다.

conda env list
# 또는,
conda info --envs

현재 활성화된 가상환경 옆에는 * 가 추가된다.

06_env_list

처음 설치했을 때는 기본값인 base 하나만 있을 것이다.

다음 명령을 통해 새 가상환경을 하나 생성한다.

# -n 옵션은 --name과 같은 것으로, 가상환경 이름을 myenv로 지정한다.
conda create -n myenv
# python=3.6 옵션은 가상환경 생성 시 파이썬 버전을 지정한다.
# 지정하지 않으면 conda에 기본 포함된 파이썬 버전으로 생성된다.
conda create -n condatorch python=3.6

# 특정 패키지 버전을 지정하면서, 그리고 패키지를 설치하면서 생성하는 것도 가능하다.
conda create -n myenv python=3.4 scipy=0.15.0 astroid babel

# 가상환경 생성 시 이것저것 깔리는 것이 싫다면 다음 옵션을 주면 된다.
conda create --no-default-packages -n myenv python

# 새 가상환경을 만들 때 특정 가상환경 안에 설치된 패키지 전부를 설치하면서 생성할 수 있다.
# base 가상환경에 있는 패키지를 전부 설치하면서 생성한다면, 
conda create -n myenv --clone base

# environment.yml 파일이 있다면 다음과 같이 생성할 수 있다.
# 생성 방법은 이후에 설명한다.
conda env create -f environment.yml

계속 진행하겠냐는 물음이 보이면 y를 입력한다.

07_env_create

다시 conda env list로 목록을 확인해보면 지정한 이름으로 가상환경이 생성되었음을 확인할 수 있다.

08_env_create

위 그림에서 activate condatorch, deactivate 등의 명령이 쓰여 있는 것을 확인할 수 있는데, 이는 특정 가상환경을 활성화 또는 비활성화할때 사용하는 명령이다(가상환경이 무엇에 쓰는 것인지 알면 무슨 말뜻인지 알 수 있을 것이다). 이는 다음 절에서 설명한다.

가상환경 삭제는 다음 명령을 통해 수행할 수 있다.

# 생성할 때와는 다르게 env를 앞에 적어주어야 한다.
# 생성 시에는 env를 앞에 적으면 실행이 되지 않는다.
# remove 앞에 env를 써 주지 않으면 가상환경 삭제가 아닌 패키지 삭제가 이루어진다.
# conda env remove -n <environment_name>
conda env remove -n condatorch
# 다음도 가능하다.
conda remove --name myenv --all

09_env_remove

Requirements.txt로 가상환경 생성하기

아래 명령들은 가독성을 위해 두 줄로 펼쳐 놓았다.

Windows 환경이라면 명령창에 다음과 같이 쓰는 것이 가능하다.

FOR /F "delims=~" %f in (requirements.txt) 
DO conda install --yes "%f" || pip install "%f"

Unix 환경이라면 다음과 같이 쓸 수 있다.

while read requirement; do conda install --yes $requirement; 
done < requirements.txt 2>error.log

conda로는 설치가 안 되고 pip으로는 설치가 되는 패키지가 있다면 다음과 같이 쓸 수 있다.

while read requirement; do conda install --yes $requirement 
|| pip install $requirement; done < requirements.txt 2>error.log

다음을 참조하였다: github 글, stackoverflow 글


가상환경 활성화, 비활성화

가상환경 활성화는 위에서도 설명했듯 다음과 같이 쓰면 된다.

activate <environment_name>
activate condatorch

Unix 등의 환경에서는 activate가 아닌 source activate를 써야 한다.

그러면 명령창의 맨 앞에 (condatorch)와 같이 활성화된 가상환경 이름이 뜬다.

비활성화는 다음 명령으로 할 수 있다.

deactivate
# 설치한 버전에 따라 deactivate는 deprecated되었다는 경고를 볼 수도 있다. 이 경우 conda deactivate이다.

10_activate

위 그림이 잘 이해가 되지 않는다면, activate를 여러 번 쓰지 않을 것을 권장한다.


가상환경 안에 패키지 설치

버전에 따라 조금씩 다른 경우도 있으나, 최신 버전(2019-02-01 기준)의 Miniconda3에서는 pip, whl, conda를 통한 설치 모두 현재 활성화된(없다면 base 또는 컴퓨터에 깔려 있는 다른 버전의 파이썬에) 가상환경에만 설치된다. 따라서 각 환경 간 거의 완전한 분리가 가능하다.

패키지 설치는 다음과 같다. pip과 거의 비슷하다.

conda install seaborn
# 여러 개를 동시에 설치할 경우 comma 없이 그냥 나열한다.
conda install numpy pandas

설치된 패키지 목록을 보고 싶으면 다음을 입력한다.

conda list

참고로 conda 환경에서도 pip 등을 통한 설치가 가능하다.

environment.yml 파일 생성 및 가상환경 생성

설치된 패키지 목록을 .yml 파일로 저장하는 명령이다.
pip freeze > requirements.txt와 같은 역할이다.

conda env export > environment.yml

만들어진 파일은 다음과 비슷하게 생겼다.

name: condatorch
channels:
  - pytorch
  - defaults
dependencies:
  - blas=1.0=mkl
  - certifi=2018.11.29=py36_0
  ...
  - zstd=1.3.7=h508b16e_0
  - pip:
    - cycler==0.10.0
    ...
    - six==1.12.0
prefix: C:\ProgramData\Miniconda3\envs\condatorch

만들어진 파일로 가상환경을 생성하는 방법은 위에서도 설명했지만 다음과 같다.

# 이 경우에는 env를 앞에 써 주어야 한다.
# -f는 --file을 의미한다.
conda env create -f environment.yml -n myenv

패키지 업데이트

특정 환경 안의 특정 패키지를 업데이트하려면 다음과 같이 하면 된다.

conda update -n <environment_name> spacy

특정 환경 안의 모든 패키지를 업데이트하려면 다음과 같이 하면 된다.

conda update -n <environment_name> --all
# 현재 환경 업데이트
conda update --all

Conda 버전 확인 및 update

명령창에서 Conda의 버전을 확인하는 방법은 다음과 같다.

conda -V
conda --version

Conda 자체를 업데이트하는 방법은 다음과 같다.

conda update conda
conda update anaconda

References

공식 홈페이지에서 더 자세한 사용법을 찾아볼 수 있다.

Comment  Read more

Jupyter Notebook 사용법(주피터 노트북 설치 및 사용법)

|

Jupyter notebook은 대화형 파이썬 인터프리터(Interpreter)로서 웹 브라우저 환경에서 파이썬 코드를 작성 및 실행할 수 있는 툴이다.
서버에 Jupyter notebook을 설치하여 포트를 개방한 후 해당 url에 접속하여 원격으로 사용하거나, 로컬 환경에서 브라우저를 띄워 대화형 환경에서 코드를 작성 및 실행할 수 있다.

설치 및 실행

설치

설치는 두 가지 방법이 있는데, 첫 번째는 Anaconda와 함께 설치하는 방법이 있다. Anaconda를 설치할 때 Jupyter Notebook도 같이 설치하면 된다.
Anaconda와 같은 역할을 하는 Miniconda 사용법은 여기를 참조하도록 한다.

Anadonda를 설치하는 방법 외에 기본적으로 pip은 Jupyter 패키지 설치를 지원한다. 설치 방법은 다른 패키지 설치 방법과 똑같다.

pip install jupyter

파이썬3과 2를 구분지어야 한다면 pip 대신 pip3를 사용한다.

실행 및 종료

jupyter notebook

01_jupyter_notebook

위 명령을 입력하면 자동으로 어떤 html 파일을 열면서 브라우저가 실행된다. 만약 실행되지 않는다면 http://localhost:8888 으로 접속하거나 위 그림의 맨 마지막 줄에 있는 url을 복사하여 브라우저에서 접속한다.

그러면 위 명령을 실행한 디렉토리 위치(위 그림에서 jupyter notebook을 실행한 줄에서 볼 수 있다. 필자의 경우 C:\JupyterTest)의 파일들이 브라우저에 보이게 된다.

02_broswer

Jupyter의 실행을 종료하려면 명령창에서 Ctrl + C를 입력한다.

03_terminate

고급: 실행 옵션

명령 옵션의 도움말을 표시한다.

jupyter notebook --help

실행 속도 상승을 위해 MathJax를 무효화할 수 있다. MathJax는 수식 입력을 위해 필요한 JavaScript 라이브러리이다.

jupyter notebook --no-mathjax

웹 브라우저를 지정하거나 실행시키지 않을 수 있다. 포트 번호 지정도 가능하다.

jupyter notebook --browser="safari"
jupyter notebook --no-browser
jupyter notebook --port=8889

노트북 실행 시 실행 디렉토리를 지정할 수 있다. 기본값은 현재 밍령창에서의 실행 위치이다.

jupyter notebook --notebook-dir=/user/define/directory

고급: 설정 파일 수정

매번 옵션을 지정해서 실행하기가 귀찮다면, Jupyter Notebook의 기본 설정을 변경하기 위해 다음 명령을 입력한다.

jupyter notebook --generate-config

그러면 Jupyter가 실행되는 대신 설정 파일이 열린다.
Linux에서는 기본적으로 /home/<username>/.jupyter/jupyter_notebook_config.py 파일로 생성되며, 윈도우에서는 C:\Users\<username>\.jupyter\jupyter_notebook_config.py로 생성된다.

설정 파일에서 필요한 옵션을 변경하여 사용하면 된다. 기본적으로 사용하지 않는 옵션은 모두 주석 처리되어 있다.

기본 설정 파일을 재지정하고 싶으면 다음과 같이 입력한다.

jupyter notebook --config=custom_config.py

임시로 설정 파일을 변경해서 실행하고 싶다면 일반 옵션 주듯이 하면 된다.

jupyter notebook --config custom_config.py

Jupyter notebook을 단순히 로컬 환경에서 실행하는 것이 아니라 서버로 띄워 놓고 원격 접속을 하려면, 위 방법으로 허용 포트나 접속 주소 등 설정 파일을 수정해야 한다.

고급: 원격 접속 설정

localhost(127.0.0.1) 말고 다른 컴퓨터에서 (서버로) 원격접속하고 싶을 때가 있다. 그럴 때는 다음 과정을 따른다.

  1. 명령창(cmd or terminal)에 python 또는 ipython을 입력하여 대화창을 연다.
    • 다음을 입력한다:
        >>> from notebook.auth import passwd
        >>> passwd()
        Enter password: 
        Verity password: 
        'sha1:c5b493745105:0d26dcd6e9cf868d3b49f43d'
      
    • 출력으로 나온 암호화된 비밀번호를 기억해 둔다.
    • 참고로 linux에서나 윈도우에서나 passwd() 등으로 비밀번호를 입력할 때에는 명령창에 입력하고 있는 문자가 전혀 표시되지 않는다. 별표(*)로도 표시되지 않으니 참고.
    • 대화창을 종료한다.
  2. 이제 조금 전에 생성한 jupyter_notebook_config.py를 편집기로 연다.
    • 아래처럼 주석처리된 부분을 다음과 같이 바꾼다. 물론 비밀번호는 조금 전 여러분이 생성한 문자열로 입력해야 한다.
        #c.NotebookApp.password = '' 
      
        c = get_config()
        c.NotebookApp.password = 'sha1:c5b493745105:0d26dcd6e9cf868d3b49f43d'
      
    • 필수: 비슷하게 다음 설정들을 바꿔주어야 한다. 모든 설정을 변경할 때에는 앞의 주석(#)을 지우도록 한다.
      • 외부접속 허용: c.NotebookApp.allow_origin = '*'
      • IP 설정: c.NotebookApp.ip = <여러분의 IP>
    • 옵션: 다음은 하고 싶으면 하도록 한다.
      • 작업경로 설정: c.NotebookApp.notebook_dir = <원하는 경로>
      • 포트 설정: c.NotebookApp.port = <원하는 port>
      • jupyter notebook으로 실행 시 브라우저 실행 여부: c.NotebookApp.open_browser = False

이제 외부접속을 할 때는 서버에서

  • jupyter notebook을 실행시킨 다음
  • <여러분의 IP="">:<원하는 port=""> 형식을 브라우저의 주소창에 입력하면 된다.
    • 예시: 123.212.321.14:8888
  • 여러분이 설정한 비밀번호를 입력한다. 암호화된 문자열이 아니라 passwd() 에서 입력한 비밀번호면 된다.
  • 물론 일반 가정집에서는 그냥 ip를 할당할 수 없기 때문에 공유기 설정을 해주거나, 회사 컴퓨터 등이라면 따로 접속 허용하는 절차를 거쳐야 한다. 이 부분은 여기서는 ~pass~
    • 그냥 되는 경우도 있다. 안 되는 경우에만 검색해서 해 보기 바람.

Jupyter의 기본 사용법

새 파일 생성

04_new

오른쪽 부분의 New 버튼을 클릭하면 Python 3, Text File, Folder, Terminal 등의 옵션이 있다(파이썬 버전에 따라 Python 2가 있을 수 있다). 우선 Python 3을 클릭하여 Python 3 코드를 입력할 수 있는 창을 열도록 한다.

05_python3

생성하면 맨 위에 기본적으로 Untitled라는 제목으로 생성이 된다. 파일 탐색기나 Finder 등에서도 Untitled.ipynb라는 파일을 확인할 수 있다.

06_ipynb

위의 checkpoints 디렉토리는 자동으로 생성된다. Jupyter는 자동저장이 되고(맨 위의 autosaved), 체크포인트를 따로 설정할 수 있다.

제목은 Untitled 부분을 클릭하면 수정할 수 있다.

편집 / 명령 모드

편집 모드에서는 셀의 내용을 편집할 수 있고(셀의 테두리가 초록색), 명령 모드는 편집중이 아닌 상태 또는 셀 자체에 조작을 가하는 상태(셀의 테두리가 파란색)이다.
명령 모드에서 편집 모드로 들어가려면 Enter키를, 반대로는 Esc 키를 누르면 된다.

셀의 타입

Code 타입, Markdown 타입이 있다.
Code 타입은 일반 코드를 실행할 수 있는 셀이다. 기본적으로 셀을 생성하면 Code 타입으로 생성된다.
Markdown 타입은 Markdown으로 셀의 내용을 작성할 수 있다. 코드로 실행되지는 않으며, 수식을 작성할 수 있다. 수식은 MathJax에 의해 지원된다. 수식 작성 방법은 여기를 참고한다.

Markdown 타입으로 변경하면 Markdown 코드를 작성할 수 있다. Shift + Enter 키를 누르면 마크다운이 실제 보여지는 방식으로 변경되며, 다시 수정하려면 Enter 또는 더블 클릭하면 편집 가능하다.

07_markdown

셀 실행

실행하고 싶은 셀의 아무 곳에나 커서를 위치시킨 후 Shift + Enter 키를 누른다.
실행하면 셀 아래쪽에는 실행 결과가 표시되고, 셀 옆의 ‘In [ ]’과 ‘Out [ ]’에 몇 번째로 실행시켰는지를 나타내는 숫자가 표시된다. 여러 번 실행하면 계속 숫자가 올라간다.

08_run

강제 중단 / 재실행

제목 아래 줄의 탭에 Kernel 탭이 있다. 커널은 IPython 대화창 아래에서 백그라운드 비슷하게 실행되는 일종의 운영체제 같은 개념이다. IPython 대화창을 관리한다고 보면 된다.

09_interrupt

Kernel 탭의 모든 버튼은 코드를 삭제하지는 않는다. 각 버튼의 기능을 설명하면,

  • Interrupt: 실행 중인 코드를 강제 중지한다. 중지하면 위 그림과 같은 에러가 뜨며 실행이 중지된다.
  • Restart: 실행 중인 코드가 중지되며 재시작된다. 코드나 실행 결과는 삭제되지 않는다.
  • Restart & Clear Output: 코드는 중지되며 실행 결과도 삭제한다.
  • Restart & Run All: 재시작 후 모든 셀의 코드를 위에서부터 순차적으로 한 번씩 실행한다.
  • Reconnect: 인터넷 연결이 끊어졌을 때 연결을 재시도한다.
  • Shutdown: 커널을 종료한다. 이 버튼을 누르면 실행 결과는 삭제되지 않으나 완전 종료된 상태로 더 이상 메모리를 잡아먹지 않는다.

Shutdown되었거나, 인터넷 연결이 끊어졌거나, 기타 문제가 있으면 아래와 같이 탭 옆에 알림이 표시된다. Shutdown 된 경우 No kernel이라고 뜬다.

10_shutdowned

현재 실행중인 커널이 있는지 확인하는 방법은 두 가지다. 첫 번째는 Home 화면에서 ipynb 파일의 아이콘이 초록색이면 실행중, 회색이면 중단된 또는 시작되지 않은 상태이다. 여기서는 해당 디렉토리에서 실행중인 것만 확인할 수 있다.

11_shutdowned

또 하나는 Home 화면에서 Files 탭 대신 Running 탭을 클릭하면 실행 중인 IPython과 터미널의 목록을 확인할 수 있다. 이 탭에서는 전체 디렉토리에서 실행중인 파일 또는 터미널을 전부 볼 수 있다.

12_list

Text File 생성

New 버튼에서 Text File을 생성하면 .txt 파일이나 .py 파일 등을 만들 수 있다. 이렇게 만든 파일은 대화 형식으로 실행되지 않고, 터미널에서 실행시켜야 한다. 읽는 것은 IPython 창에서도 가능하다.

Folder 생성

디렉토리를 생성할 때 사용한다. 폴더랑 같은 것이다.

터미널

New 버튼으로 Terminal을 클릭하면, 터미널을 하나 새로 연다. 이것은 윈도우나 맥 등의 명령창(cmd 또는 terminal)과 같다. 여기서 .py 파일을 실행시킬 수 있고, 파일의 목록을 보거나 삭제하는 등의 명령이 모두 가능하다. Running 탭에서 중지시킬 수 있다.

13_terminal

파일 이름 변경 또는 삭제

파일 맨 왼쪽의 체크박스를 클릭하면 복제, 수정, 삭제 등이 가능하다. 물론 로컬 파일 탐색기에서 수정이나 삭제를 해도 되며, 서버가 연결에 문제가 없으면 바로 반영된다.

14_name

자동완성

웬만한 IDE에는 다 있는 자동완성 기능이다. 변수나 함수 등을 일부만 입력하고 Tab 키를 누르면 된다. 따로 설명할 필요는 없을 듯 하다.


단축키

단축키 정보는 [Help] - [Keyboard Shortcuts] 또는 명령 모드에서 H를 눌러서 표시할 수 있다.

공용 단축키 설명
Shift + Enter 액티브 셀을 실행하고 아래 셀을 선택한다.
Ctrl + Enter 액티브 셀을 실행한다.
Alt + Enter 액티브 셀을 실행하고 아래에 셀을 하나 생성한다.
편집 모드 단축키 설명
Ctrl + Z Undo 명령이다.
Ctrl + Shift + Z Redo 명령이다.
Tab 자동완성 또는 Indent를 추가한다.
Shift + Tab 툴팁 또는 변수의 상태를 표시한다.
Ctrl + Shift + - 커서의 위치에서 셀을 잘라 두 개로 만든다.

참고로 명령 모드 단축키 중 콤마(,)로 되어 있는 것은 연속해서 누르라는 의미이다. 예로 D를 두 번 누르면 액티브 코드 셀을 삭제한다.

명령 모드 단축키 설명
↑, ↓ 셀 선택
A 액티브 코드 셀의 위(Above)에 셀을 하나 생성한다.
B 액티브 코드 셀의 위(Below)에 셀을 하나 생성한다.
Ctrl + S Notebook 파일을 저장한다.
Shift + L 줄 번호 표시를 토글한다.
D, D (D 두번 연속으로 타이핑)액티브 코드 셀을 삭제한다.
Z 삭제한 셀을 하나 복원한다.
Y 액티브 코드 셀을 Code 타입(코드를 기술하는 타입)으로 한다.
M 액티브 코드 셀을 Markdown 타입으로 한다.
O, O 커널을 재시작한다.
P 명령 팔레트를 연다.
H 단축키 목록을 표시한다. Enter 키로 숨긴다.

Jupyter의 기능

DocString의 표시

선언한 변수 뒤에 ?를 붙여서 셀을 실행하는 것으로 해당 변수의 상태를 확인할 수 있다.

약간 다른 방법으로 변수를 타이핑한 후 Shift + Tab을 누르면 툴팁이 표시된다.
툴팁에는 DocString의 일부 내용이 표시된다.

이미지 첨부하기

Drag & Drop으로 첨부할 수 있다.

shell(명령 프롬프트)의 이용

명령창에서 쓰는 명령을 그대로 쓰되, 맨 앞에 !를 입력하여 사용 가능하다.

!cd Documents

매직 명령어 이용

맨 앞에 %를 붙이고 특정 명령을 수행할 수 있다. 이는 파이썬 문법에는 포함되지 않은, Jupyter notebook만의 기능이다.

15_magic

매직 명령어 설명
%pwd 현재 디렉토리 경로 출력
%time 코드 코드의 실행 시간을 측정하여 표시
%timeit 코드 코드를 여러 번 실행한 결과를 요약하여 표시
%history -l 3 최근 3개의 코드 실행 이력 취득
%ls 윈도우의 dir, Linux의 ls 명령과 같음
%autosave n 자동저장 주기를 설정한다. 초 단위이며, 0이면 무효로 한다.
%matplotlib 그래프를 그리는 코드 위에 따로 설정한다. %matplotlib inline으로 설정하면 코드 셀의 바로 아래에, %matplotlib tk로 설정하면 별도 창에 그래프가 출력된다. %matplotlib notebook으로 하면 코드 셀 바로 아래에 동적으로 그래프를 조작할 수 있는 그래프가 생성된다.
# 코드 실행 시간 측정
%time sum(range(10000))
# 결과:
# CPU times: user 225 us, sys: 0 ns, total: 225 us
# Wall time: 228 us
# 499950000

# 1000회 반복, 3회 실행
%timeit sum(range(10000))
# 결과: 
# 1000 loops, best of 3: 238 us for loop

# 옵션 지정하기
%timeit -n 2000 -r 5 sum(range(10000))

# 셀 전체의 시간 측정
%%timeit -n 1000 -r 3
s = 0
for i in range(10000):
    s += i
Comment  Read more