안경잡이개발자

728x90
반응형

  최근에 GAN을 다시 공부하면서, GAN을 학습할 때 유의할 점에 대해서 다시 한번 정리하고 있다. 지금까지 GAN 네트워크를 수백 번 이상 학습을 해보았지만, 역시 학습 난이도가 높다. 그래서 경험적으로 GAN을 학습할 때, 어떤 테크닉을 사용하는 것이 유리한지 정리해 둘 필요가 있다.

 

※ Batch Size ※

 

  GAN에서는 배치 사이즈(batch size)에 의하여 결과가 많이 바뀌는 경향이 있다. 일반적으로 생성 모델(generative model)이 아닌 분류 모델에서는 배치 사이즈를 크게 설정하면, 그만큼 학습 속도가 빨라지게 되는 장점이 있다. 예를 들어 batch size가 64일 때보다 multi GPU를 활용하여 batch size 256으로 키워서 사용할 수 있으며, 그만큼 num_workers와 learning rate도 증가시킬 수 있다. 그러면 전체 학습 속도가 2~3배 빨라지는 것을 경험할 수 있다. 물론 분류 모델에서도 batch size가 1,024 이상으로 과도하게 커지는 경우 성능이 하락하는 현상을 볼 수도 있는데, GAN에서는 이러한 문제가 더 크게 발생하는 경향이 있다.

 

  실제로 필자가 GAN을 학습할 때는 상당수의 데이터셋에서 batch size를 64 이하로 설정하는 것이 결과적으로 성능이 좋았다. 필자는 예전에 분류 모델을 학습할 때 batch size를 증가시키고, 그만큼 learning rate을 크게 설정했던 것을 기억하여 GAN에서도 동일하게 적용해 보았으나, GAN에서는 batch size를 키웠을 때 성능 하락이 발생하는 문제를 자주 경험했다. 당연히 데이터셋마다 다르지만, 많은 경우에서 batch size가 64 이하일 때 합리적인 성능이 나왔다. 그렇기 때문에 사실상 multi GPU를 활용하기 어려운 경우가 많았다.

 

  또한 가끔은 잘 동작하는 코드에서 오직 multi GPU 설정만 적용했을 때에도 성능이 떨어지는 현상을 경험한 적이 있다. 다시 말해 batch size나 learning rate 등을 그대로 쓰는 상황에서 단지 multi GPU만 적용했을 뿐인데도 문제가 발생했다. 정확한 이유는 잘 모르겠지만, DCGAN처럼 convolution layer를 활용하는 경우에는 문제가 덜했다. 사실 batch size가 작을 때는 multi GPU를 사용할 이유가 없기 때문에, 단순히 1개의 GPU에서 작은 batch size로 학습을 진행하는 게 학습상의 효율성이 컸던 경우가 많다.

 

※ Conditional GAN ※

 

  학습이 잘 안 되거나 mode collapse의 문제가 발생하는 경우 conditional GAN을 활용하는 것이 GAN의 성능을 높이는 데에 도움을 주기도 한다. 기본적으로 GAN은 학습 난이도가 높기 때문에, 추가적인 guide가 존재하는 경우 성능이 향상될 수 있다. 따라서 단순히 데이터만 있는 것이 아니라, 레이블 정보가 같이 있다면 레이블 정보도 활용해 보는 것이 좋다. 필자의 경우 실제로 MNIST를 학습할 때, 단순히 GAN을 쓰는 것보다 conditional GAN을 쓸 때 mode collapse의 문제가 덜하고, 생성하고자 하는 이미지를 쉽게 컨트롤할 수 있었던 기억이 있다.

 

출처: https://arxiv.org/abs/1411.1784

 

  실제로 conditional GAN을 이용해 MNIST를 학습하면 다음과 같이 각 레이블(label)마다 이미지를 생성할 수 있게 된 것을 확인할 수 있다. 모드(mode)를 컨트롤할 수 있도록 해줌으로써, 상대적으로 수월하게 학습할 수 있을 뿐더러 학습 이후에 원하는 레이블의 숫자 이미지를 샘플링할 수 있다.

 

 

※ GAN의 Loss ※

 

  GAN에는 정말 다양한 종류의 손실(loss) 함수가 있다. 많은 논문에서 일반적으로 사용하는 loss로는 WGAN-GP가 있다. 기본적인 original GAN loss와 비교하여 WGAN-GP는 더 좋은 결과를 낼 때가 많다. 하지만 데이터셋에 따라서 더 좋은 loss 세팅이 존재하는 경우가 있다. 실제로 어떤 GAN 아키텍처를 사용하느냐에 따라서 WGAN-GP보다 오히려 LSGAN이나 일반적인 GAN loss가 더 좋은 FID를 보이기도 한다. 따라서 데이터셋마다 많은 시간을 투자해 하이퍼 파라미터(learning rate, optimizer 등)를 튜닝해보는 것이 좋다.

 

  필자의 경우 흑백 사진 데이터셋에 대하여 MLP 아키텍처를 사용할 때, 기본적인 original GAN loss보다 WGAN-GP 손실을 사용할 때 훨씬 성능이 개선되었다. 반면에 DCGAN의 경우에는 WGAN-GP를 사용할 때보다 LSGAN을 사용할 때가 오히려 성능이 더 좋았던 경험이 있다. 그래서 WGAN-GP를 무작정 사용하기보다는, 잘 동작한다고 알려진 네트워크를 사용할 필요가 있다. 예를 들어 ResNet 기반의 네트워크가 WGAN-GP와 함께 쓰이는 것을 자주 확인할 수 있다. 또한 CelebA나 FFHQ와 같은 데이터셋은 WGAN-GP loss가 효과적이라고 알려져 있다.

 

  또한 WGAP-GP의 경우 판별자(discriminator)를 여러 번 업데이트하고, 생성자(generator)를 한 번 업데이트하는 방식을 자주 사용한다. 그래서 실제로 학습이 되는 것을 보면서 d를 업데이트하는 횟수를 설정할 필요가 있다. 만약에 D를 너무 많이 업데이트하고 (n_critic이 너무 높은 경우), G를 한 번 업데이트하는 경우 D가 상대적으로 너무 강해져서, 진짜/가짜 이미지를 너무 쉽게 판별하게 될 수 있다. 이 경우 G또한 더 이상 정상적으로 업데이트되지 못한다. 배치 사이즈가 클 때도 D가 너무 강해지는 경향이 있으며, G와 D의 네트워크 capacity 밸런스 또한 맞추어 줄 필요가 있다.

 

출처: https://arxiv.org/abs/1704.00028

 

  그리고 GAN loss마다 사용하면 안 되는 레이어 유형이 존재하기도 한다. 예를 들어 WGAN-GP의 경우 discriminator에서 batch normalization 대신에 instance normalization을 사용하는 편이 더 유리하다. 실제로 batch normalization이 포함된다는 점에서 수렴이 잘 안 되어 노이즈와 같은 결과 이미지만 내보내던 GAN 네트워크에 대하여 batch normalization을 제거하고, 다른 normalization 레이어를 추가했을 때 비로소 수렴하여 좋은 이미지를 만드는 것을 확인했던 경험이 있다.

 

출처: https://towardsdatascience.com/gan-objective-functions-gans-and-their-variations-ad77340bce3c

 

  또한 GAN을 활용한 image-to-image translation 기법에서는 ground-truth 이미지와 유사해질 수 있도록 하기 위해서 L1을 활용한 loss가 자주 사용된다. 일반적으로 L1이나 L2 모두 blurry한 결과를 만들 수 있다는 단점이 있지만, 그나마 L1을 사용하면서 GAN과 함께 적용했을 때 결과가 조금 더 선명하게 나온다는 점을 언급한 논문들(Pix2Pix 등)이 있다.


※ 최적화 ※

  학습이 잘 되지 않거나, mode collapse가 발생하는 경우 학습률(learning rate)을 조절해 볼 필요가 있다. 또한 일반적으로 GAN에서는 Adam optimizer가 많이 사용된다. 하지만 이 또한 어떠한 loss를 사용하는지에 따라서 조금씩 차이가 있다. 그리고 충분히 수렴한 뒤에는 epoch을 증가시켜도 생성되는 이미지의 quality가 더 이상 개선되지 않는다. 따라서 epoch이 증가함에 따라서 FID를 계산해보는 방식으로 학습의 진행 과정을 확인해 볼 수도 있다.

 

※ Feature Matching ※

 

  feature matching을 사용해 GAN 모델의 불안정성을 완화하고 좋은 결과를 낼 수 있다.

728x90
반응형

Comment +0

728x90
반응형

  Java로 간단한 프로그램을 작성하여 실행했다. 다만 동일한 함수를 30만 번 재귀적으로 호출했더니 다음과 같이 Stack Overflow Error가 발생했다. 오류는 일반적으로 다음과 같이 "java.lang.StackOverflowError" 형태로 출력되는 것을 알 수 있다. 기본적으로 30만 번 정도의 호출은 감당할 수 있을 정도로 기본적인 Stack Size를 키우고 싶다면 어떻게 해야 할까? 참고로 Heap 관련 오류로는 "java.lang.OutOfMemoryError: Java heap space" 등의 오류가 있다.

 

 

※ JVM 기본 플래그(flag) 값 확인 ※

 

  명령 프롬프트(CMD)를 실행한 뒤에 다음과 같은 명령어를 입력한다. 그러면 Stack이나 Heap이라는 문자열이 포함된 플래그(flag)를 확인할 수 있다.

 

java -XX:+PrintFlagsFinal -version | findstr "Stack Heap"

 

  명령어 실행 결과 예시는 다음과 같다. 일반적으로 Stack 크기와 관련해서는 ThreadStackSize를 확인한다.

 

 

  다만 필자처럼 윈도우(Windows) 운영체제를 사용하는 경우 ThreadStackSize 값이 0으로 나오는 문제가 있다. 기본적으로 Windows에서는 ThreadStackSize의 값이 가상 메모리에 따라서 다르게 적용된다고 알려져 있다. 그래서 Xss 옵션으로 Stack Size를 변경한다고 해도, 이것이 실제로 기본값보다 크게 만드는 것인지 혹은 작게 만드는 것인지 확실치 않았다. 그래서 필자의 경우에는 여러 번 시도해 보면서 적절한 크기를 찾았다. Stack Size와 관련하여 오라클(Oracle)에서 제공하는 참고 문서는 다음과 같다.

 

  참고: https://docs.oracle.com/cd/E13150_01/jrockit_jvm/jrockit/jrdocs/refman/optionX.html#wp1024112

 

  JVM의 옵션(option)-X{option} 형태로 적용할 수 있다. 예를 들어 java -Xss1m은 각 쓰레드(thread)에 할당되는 최대 스택 크기를 1m로 설정하기 위해 사용할 수 있다. 기본적으로 각 쓰레드(thread)에는 하나 이상의 스택이 존재한다. 따라서 필자가 현재 경험한 문제의 경우 Xss256m이나 Xss512m 옵션으로 문제를 해결할 수 있는 것이다. 아무튼 Main 클래스(class)를 -Xss256m 옵션과 함께 실행하기 위해서는 다음과 같이 입력할 수 있다.

 

java -Xss256m Main.java

 

  혹은 애초에 다음과 같이 튜닝 플래그를 사용하여 다음과 같은 방식으로 스택 크기를 설정할 수도 있다. 

 

java -XX:ThreadStackSize=1m Main.java

 

  참고로 특정한 JVM의 경우 최솟값보다 작은 크기로 설정하는 것을 허용하지 않기도 한다. 예를 들어 다음과 같이 Stack Size를 1k로 설정하려고 하면, 최소한 180k 이상의 Stack Size를 가져야한다고 나오는 것을 알 수 있다.

 

 

  아무튼, 결과적으로 다음과 같이 실행하여 Stack Overflow 발생 없이 프로그램을 실행할 수 있었다.

 

java -Xss256m Main.java
728x90
반응형

Comment +0

728x90
반응형

  프로그래밍/알고리즘 문제를 출제할 때 C++을 이용하는 경우에 testlib.h를 사용하면 좋습니다. 실제로 상당수의 올림파이드 문제와 ACM-ICPC 대회 문제가 testlib.h를 활용하여 출제되고 있습니다. testlib.h는 단일 헤더파일 형태로 단순히 testlib.h 파일을 복사하여 프로젝트 폴더에 붙여넣기하면 끝입니다. testlib.h에는 다음과 같은 기능들이 포함되어 있습니다. 일반적인 문제의 경우 GeneratorValidator가 가장 기본적이면서 중요합니다.

 

  1. Generator: 테스트 케이스(test case)를 생성하는 기능입니다. 데이터의 개수가 100,000개를 넘어가면, 일일이 손으로 문자를 입력할 수 없기 때문에, 이러한 Generator는 반드시 필요합니다.

  2. Validator: 테스트 케이스(test case)가 정확하며 문제의 제약 조건(constraint)을 충족하는지 검사하는 기능입니다. 문자열이 0으로 시작하는지, 특정 수 범위를 벗어나지 않는지 등을 검사할 수 있습니다.

  3. Interatctor: 문제가 interactive 문제일 때 사용할 수 있는 기능입니다.

  4. Checkers: 문제가 여러 개의 정답을 허용하는 경우, 참가자의 정답이 정상적인지 판단하는 추가적인 프로그램을 작성해야 합니다.

 

  ▶ CodeForces testlib.h 소개: https://codeforces.com/testlib

 

Testlib - Codeforces

 

codeforces.com

 

  전체 소스코드는 GitHub 저장소에 업로드되어 있으며, 기본적인 사용 방법 또한 이 경로에서 확인 가능합니다.

 

  ▶ Testlib GitHub 링크: https://github.com/MikeMirzayanov/testlib

 

GitHub - MikeMirzayanov/testlib: Automatically exported from code.google.com/p/testlib

Automatically exported from code.google.com/p/testlib - GitHub - MikeMirzayanov/testlib: Automatically exported from code.google.com/p/testlib

github.com

 

  따라서 다음과 같이 프로젝트 폴더를 구성한 뒤에 소스코드를 작성하시면 됩니다.

 

 

※ 테스트 케이스 생성하기 ※

 

  입출력 데이터셋을 생성할 때는 Generator 기능을 사용합니다. 아래의 경로를 참고하는 것을 추천합니다.

 

  ▶ Testlib의 Generator 설명: https://codeforces.com/blog/entry/18291

 

Generators with testlib.h - Codeforces

 

codeforces.com

 

  1. 랜덤 정수 및 단어 생성하기

 

#include <bits/stdc++.h>
#include "testlib.h"

using namespace std;

int main(int argc, char* argv[]) {
    registerGen(argc, argv, 1);

    cout << rnd.next(1, 10) << '\n'; // 값의 범위가 [1, 10]인 랜덤 정수 생성
    cout << rnd.next("[a-zA-Z0-9]{1,100}") << '\n'; // 길이가 [1, 100]인 랜덤 문자열 생성
    return 0;
}

 

  참고로 이 프로그램을 실행할 때는 첫 번째 인자(argument)로 넣은 값이 시드(seed)가 됩니다. 따라서 다음과 같이 인자 값을 바꾸어 실행하면, 서로 다른 랜덤 결과가 출력됩니다.

 

 

  2. 랜덤 순열(permutation) 생성하기

 

  순열을 생성할 때는 rnd.perm() 함수를 사용할 수 있습니다. 다음 코드는 첫 번째 인자를 랜덤 시드로, 두 번째 인자를 순열의 길이로 설정하는 코드입니다.

 

#include <bits/stdc++.h>
#include "testlib.h"

using namespace std;

int main(int argc, char* argv[]) {
    registerGen(argc, argv, 1);
    
    int n = opt<int>(2);
    println(n);
    println(rnd.perm(n, 1));
}

 

 

  3. 트리(tree) 생성하기

 

  다음과 같이 트리를 생성할 수 있다.

 

#include <bits/stdc++.h>
#include "testlib.h"

using namespace std;

int main(int argc, char* argv[]) {
    registerGen(argc, argv, 1);

    int n = atoi(argv[1]);
    int t = atoi(argv[2]);
    ofstream outFile(argv[3]);

    vector<int> p(n);
    /* setup parents for vertices 1 ... n-1 */
    for(int i = 0; i < n; ++i)
        if (i > 0)
            // t가 0이면 일반적인 트리
            // t가 양수값이면 편향적인 트리
            // t가 음수값이면 높이가 낮은 트리
            p[i] = rnd.wnext(i, t);
    outFile << n << '\n';

    /* shuffle vertices 1 ... n-1 */
    vector<int> perm(n);
    for(int i = 0; i < n; ++i)
        perm[i] = i;
    shuffle(perm.begin() + 1, perm.end());

    /* put edges considering shuffled vertices */
    vector<pair<int,int> > edges;
    for (int i = 1; i < n; i++)
        if (rnd.next(2))
            edges.push_back(make_pair(perm[i], perm[p[i]]));
        else
            edges.push_back(make_pair(perm[p[i]], perm[i]));

    /* shuffle edges */
    shuffle(edges.begin(), edges.end());
    for (int i = 0; i + 1 < n; i++)
        outFile << edges[i].first + 1 << ' ' << edges[i].second + 1 << '\n';

    outFile.close();
    return 0;
}

 

※ 테스트 케이스 검증하기 ※

 

  입출력 데이터셋을 검증할 때는 Validator 기능을 사용합니다. 테스트 케이스 검증에 있어서 사용되는 함수들은 다음의 경로에서 확인하는 것을 추천합니다.

 

  ▶ Testlib의 Validator 설명: https://codeforces.com/blog/entry/18426

 

Validators with testlib.h - Codeforces

 

codeforces.com

 

 

  예를 들어 필자가 출제했던 문제인 18310번 안테나 문제의 Validator는 다음과 같이 작성할 수 있습니다.

 

  ▶ 18310번 안테나 문제: https://www.acmicpc.net/problem/18310

 

18310번: 안테나

첫째 줄에 집의 수 N이 자연수로 주어진다. (1≤N≤200,000) 둘째 줄에 N채의 집에 위치가 공백을 기준으로 구분되어 1이상 100,000이하의 자연수로 주어진다.

www.acmicpc.net

 

#include <bits/stdc++.h>
#include "testlib.h"

using namespace std;

int main(int argc, char* argv[]) {
    registerValidation(argc, argv);
    
    int n = inf.readInt(1, 200000, "n");
    inf.readEoln(); // EOLN(한 줄의 끝) 읽기
    
    for (int i = 0; i < n; i++) {
        int x = inf.readInt(1, 100000, "x");
        if (i < n - 1) inf.readSpace(); // Space(공백) 읽기
    }
    inf.readEoln(); // EOLN(한 줄의 끝) 읽기

    inf.readEof(); // 파일의 끝(Validator 마지막 부분)
}

 

  혹은 다음과 같이 작성할 수도 있습니다.

 

#include <bits/stdc++.h>
#include "testlib.h"

using namespace std;

int main(int argc, char* argv[]) {
    registerValidation(argc, argv);
    
    int n = inf.readInt(1, 200000, "n");
    inf.readEoln(); // EOLN(한 줄의 끝) 읽기
    
    inf.readInts(n, 1, 100000); // n개의 구분된 [1, 100000] 범위의 정수들 입력
    inf.readEoln(); // EOLN(한 줄의 끝) 읽기

    inf.readEof(); // 파일의 끝(Validator 마지막 부분)
}

 

  개인 콘솔에서 실행할 때는 다음과 같이 실행이 가능합니다.

 

 

  ▶ 18511번 큰 수 구성하기 문제: https://www.acmicpc.net/problem/18511

 

18511번: 큰 수 구성하기

첫째 줄에 N, K의 원소의 개수가 공백을 기준으로 구분되어 자연수로 주어진다. (10 ≤ N ≤ 100,000,000, 1 ≤ K의 원소의 개수 ≤ 3) 둘째 줄에 K의 원소들이 공백을 기준으로 구분되어 주어진다. 각

www.acmicpc.net

 

  큰 수 구성하기 문제는 다음과 같이 검증 코드를 작성할 수 있습니다. 각 원소의 값이 1부터 9까지의 자연수이기 때문에, 항상 K의 원소로만 구성된 N보다 작거나 같은 자연수를 만들 수 있습니다.

 

#include <bits/stdc++.h>
#include "testlib.h"

using namespace std;

int main(int argc, char* argv[]) {
    registerValidation(argc, argv);
    
    int n = inf.readInt(10, 100000000, "n");
    inf.readSpace(); // Space(공백) 읽기
    int k = inf.readInt(1, 3, "k");
    inf.readEoln(); // EOLN(한 줄의 끝) 읽기

    inf.readInts(k, 1, 9); // k개의 구분된 [1, 9] 범위의 정수들 입력
    inf.readEoln(); // EOLN(한 줄의 끝) 읽기

    inf.readEof(); // 파일의 끝(Validator 마지막 부분)
}

 

  ▶ 18311번 왕복 문제: https://www.acmicpc.net/problem/18311

 

18311번: 왕복

첫째 줄에 정수 N, K가 공백을 기준으로 구분되어 주어진다. (1≤N≤100,000) 단, K는 항상 왕복 거리보다 작은 양의 정수 혹은 0으로 주어진다. 둘째 줄에 1번부터 N번까지 각 코스의 길이가 공백을

www.acmicpc.net

 

  본 문제처럼 추가적인 제약 조건(constraint)가 있는 경우에는 ensuref() 함수를 사용할 수 있습니다.

 

#include <bits/stdc++.h>
#include "testlib.h"

using namespace std;

int main(int argc, char* argv[]) {
    registerValidation(argc, argv);
    
    int n = inf.readInt(1, 100000, "n");
    inf.readSpace(); // Space(공백) 읽기
    long long k = inf.readLong(0, 4999999999LL, "m");
    inf.readEoln(); // EOLN(한 줄의 끝) 읽기

    // n개의 구분된 [1, 50000] 범위의 정수들 입력
    vector<int> v = inf.readInts(n, 1, 50000);
    inf.readEoln(); // EOLN(한 줄의 끝) 읽기

    long long summary = accumulate(v.begin(), v.end(), 0LL);
    ensuref(k < summary * 2, "K(%lld) is should be lower than summary * 2(%lld).", k, summary * 2);

    inf.readEof(); // 파일의 끝(Validator 마지막 부분)
}

 

  ▶ 18405번 경쟁적 전염: https://www.acmicpc.net/problem/18405

 

18405번: 경쟁적 전염

첫째 줄에 자연수 N, K가 공백을 기준으로 구분되어 주어진다. (1 ≤ N ≤ 200, 1 ≤ K ≤ 1,000) 둘째 줄부터 N개의 줄에 걸쳐서 시험관의 정보가 주어진다. 각 행은 N개의 원소로 구성되며, 해당 위치

www.acmicpc.net

 

  입력이 2차원 배열 형태로 주어질 때에도 다음과 같이 Validator를 사용할 수 있습니다.

 

#include <bits/stdc++.h>
#include "testlib.h"

using namespace std;

int main(int argc, char* argv[]) {
    registerValidation(argc, argv);

    int n = inf.readInt(1, 200, "n");
    inf.readSpace(); // Space(공백) 읽기
    int k = inf.readInt(1, 1000, "k");
    inf.readEoln(); // EOLN(한 줄의 끝) 읽기

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            inf.readInt(0, k, "virus");
            if (j != n - 1) inf.readSpace(); // Space(공백) 읽기
        }
        inf.readEoln(); // EOLN(한 줄의 끝) 읽기
    }

    int s = inf.readInt(0, 10000, "s");
    inf.readSpace(); // Space(공백) 읽기
    int x = inf.readInt(1, n, "x");
    inf.readSpace(); // Space(공백) 읽기
    int y = inf.readInt(1, n, "y");
    inf.readEoln(); // EOLN(한 줄의 끝) 읽기

    inf.readEof(); // 파일의 끝(Validator 마지막 부분)
}

 

  ▶ 18427번 함께 블록 쌓기: https://www.acmicpc.net/problem/18427

 

18427번: 함께 블록 쌓기

첫째 줄에 자연수 N, M, H가 공백을 기준으로 구분되어 주어진다. (1 ≤ N ≤ 50, 1 ≤ M ≤ 10, 1 ≤ H ≤ 1,000) 둘째 줄부터 N개의 줄에 걸쳐서 각 학생이 가진 블록들의 높이가 공백을 기준으로 구

www.acmicpc.net

 

  문자열을 처리해야 하는 경우에도 마찬가지로 사용할 수 있습니다.

 

#include <bits/stdc++.h>
#include "testlib.h"

using namespace std;

int main(int argc, char* argv[]) {
    registerValidation(argc, argv);

    int n = inf.readInt(1, 50, "n");
    inf.readSpace(); // Space(공백) 읽기
    int m = inf.readInt(1, 10, "m");
    inf.readSpace(); // Space(공백) 읽기
    int h = inf.readInt(1, 1000, "h");
    inf.readEoln(); // EOLN(한 줄의 끝) 읽기

    for (int i = 0; i < n; i++) {
        string s;
        getline(cin, s);
        stringstream ss(s);
        string tmp;
        // 공백을 기준으로 구분된 정수를 읽으며
        while (ss >> tmp) {
            ensuref(atoi(tmp.c_str()) <= 1000, "Each height(%d) is should be <= 1,000.", atoi(tmp.c_str()));
        }
    }

    inf.readEof(); // 파일의 끝(Validator 마지막 부분)
}

 

  ▶ 18428번 감시 피하기: https://www.acmicpc.net/problem/18428

 

18428번: 감시 피하기

NxN 크기의 복도가 있다. 복도는 1x1 크기의 칸으로 나누어지며, 특정한 위치에는 선생님, 학생, 혹은 장애물이 위치할 수 있다. 현재 몇 명의 학생들은 수업시간에 몰래 복도로 빠져나왔는데, 복

www.acmicpc.net

 

  이 문제에서는 각 문자가 'X', 'S', 'T' 중에 하나로 입력되어야 합니다.

 

#include <bits/stdc++.h>
#include "testlib.h"

using namespace std;

int main(int argc, char* argv[]) {
    registerValidation(argc, argv);

    int n = inf.readInt(3, 6, "n");
    inf.readEoln(); // EOLN(한 줄의 끝) 읽기

    int t_count = 0;
    int s_count = 0;
    int x_count = 0;

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            string tmp = inf.readToken("[STX]{1, 1}"); // 'S', 'T', 'X' 중에 한 문자로 입력
            if (tmp == "T") t_count++;
            if (tmp == "S") s_count++;
            if (tmp == "X") x_count++;
            if (j != n - 1) inf.readSpace(); // Space(공백) 읽기
        }
        inf.readEoln(); // EOLN(한 줄의 끝) 읽기
    }

    ensuref(t_count <= 5, "The number of teachers(%d) is should be <= 5.", t_count);
    ensuref(s_count <= 30, "The number of students(%d) is should be <= 30.", s_count);
    ensuref(x_count >= 3, "The number of blanks(%d) is should be >= 3.", x_count);

    inf.readEof(); // 파일의 끝(Validator 마지막 부분)
}

 

  ▶ 18352번 특정 거리의 도시 찾기: https://www.acmicpc.net/problem/18352

 

18352번: 특정 거리의 도시 찾기

첫째 줄에 도시의 개수 N, 도로의 개수 M, 거리 정보 K, 출발 도시의 번호 X가 주어진다. (2 ≤ N ≤ 300,000, 1 ≤ M ≤ 1,000,000, 1 ≤ K ≤ 300,000, 1 ≤ X ≤ N) 둘째 줄부터 M개의 줄에 걸쳐서 두 개

www.acmicpc.net

 

  입력이 그래프 형태로 들어올 때도 Validator 기능을 사용할 수 있습니다.

 

#include <bits/stdc++.h>
#include "testlib.h"

using namespace std;

int main(int argc, char* argv[]) {
    registerValidation(argc, argv);

    int n = inf.readInt(2, 300000, "n");
    inf.readSpace(); // Space(공백) 읽기
    int m = inf.readInt(1, 1000000, "m");
    inf.readSpace(); // Space(공백) 읽기
    int k = inf.readInt(1, 300000, "k");
    inf.readSpace(); // Space(공백) 읽기
    int x = inf.readInt(1, n, "x");
    inf.readEoln(); // EOLN(한 줄의 끝) 읽기

    for (int i = 0; i < m; i++) {
        int a = inf.readInt(1, n, "a");
        inf.readSpace(); // Space(공백) 읽기
        int b = inf.readInt(1, n, "b");
        ensuref(a != b, "A(%d) and B(%d) are should be different.", a, b);
        inf.readEoln(); // EOLN(한 줄의 끝) 읽기
    }

    inf.readEof(); // 파일의 끝(Validator 마지막 부분)
}
728x90
반응형

Comment +0

728x90
반응형

  깃허브(GitHub)는 단순히 아이디, 비밀번호를 입력하여 인증하는 방식을 없애고 있는 추세다. 물론 GitHub 공식 웹 사이트에 로그인할 때는 ID와 비밀번호를 입력한 뒤에 SMS 인증을 진행하여 로그인할 수 있다. 하지만, 별도로 리눅스 서버에서 이따금 내 GitHub 계정의 비공개(private) 저장소의 코드를 가져올 때(Pull)는 단순히 아이디와 비밀번호로는 가져올 수 없다. 단순히 아이디와 비밀번호를 입력해 로그인하는 경우에는 다음과 같은 오류가 등장한다.


  "Can't connect to any URI: ..."

  이럴 때는 Personal Access Token을 이용해 인증을 진행하면 된다. ID와 비밀번호 대신에 ID와 Personal Access Token을 이용하는 방식인데, 그냥 저장소(repository)의 코드를 불러올 때 비밀번호 입력하는 칸에 Personal Access Token을 입력하면 된다. 굉장히 간단하다.

 

※ Personal Access Token 생성 방법 ※

 

  Personal Access Token을 새롭게 생성하는 방법은 간단하다. GitHub에 접속한 뒤에 [Settings] 페이지로 이동한다.

 

 

  이후에 [Developer settings] 페이지로 이동한다.

 

 

  그리고 다음과 같이 [Pesonal access tokens] 페이지로 이동하면 된다. 참고로 아래 링크를 클릭하여 바로 접근할 수도 있다.

 

  ▶ Pesonal Access Token 페이지 경로: https://github.com/settings/tokens

 

GitHub: Where the world builds software

GitHub is where over 65 million developers shape the future of software, together. Contribute to the open source community, manage your Git repositories, review code like a pro, track bugs and feat...

github.com

 

  이후에는 다음과 같이 [Generate new token] 버튼을 눌러 새로운 토큰(token)을 생성하면 된다.

 

 

  이후에 다음과 같이 토큰의 이름과 토큰의 범위(scope)를 설정한 뒤에 토큰을 생성하면 된다. 간단히 private repository에 접근하고 관리할 수 있는 권한을 주기 위해서는 [repo]에 체크하면 된다. 또한 만료(expiration) 기한을 설정할 수 있는데, 단순히 무기한으로 설정하는 것은 보안상의 이슈가 존재할 수 있다. 따라서 실제로 해당 토큰이 필요한 기간을 계산하여 생성할 필요가 있다.

 

 

  토큰이 생성되면 다음과 같이 토큰(token) 문자열을 복사한 뒤에 별도로 저장해 두고 있으면 된다. 참고로 토큰은 단 한 번만 출력되기 때문에, 생성 직후에 토큰 문자열을 저장해 두지 않으면 해당 토큰(token)을 다시 확인할 수 없다. 따라서 꼭 토큰 문자열을 복사하여 별도의 파일로 저장해 두자.

 

 

  결과적으로 이렇게 만들어진 토큰(token)을 비밀번호(password) 대신 입력하여 로그인을 진행할 수 있다.

 

※ Personal Access Token 삭제하는 방법 ※

 

  삭제할 때는 마찬가지로 [Personal access tokens] 페이지로 접속한 뒤에 [Delete] 버튼을 누르면 된다.

 

728x90
반응형

Comment +0