반응형


 

 NLTK 사용하여 .txt파일 문장 단위로 쪼개기

 

딥러닝 모델들이 사용하는 Corpus 파일들은 대부분 Sentence 단위로 학습을 진행하고, 이를 위해서라도 Sentence 단위별로 나눠주는 것이 편리하다. NLTK를 사용하여 문장을 나누고, 이 과정에서 발생할 수 있는 문제를 해결해보자.

 

Requirements


$pip install nltk
$python -m nltk.downloader all

 

NLTK는 대표적인 파이썬 자연어 처리 라이브러리로, 전처리에 필요한 수많은 기능이 포함되어있다.

https://github.com/nltk/nltk

 

GitHub - nltk/nltk: NLTK Source

NLTK Source. Contribute to nltk/nltk development by creating an account on GitHub.

github.com

 

단점을 꼽자면, 굉장히 많은 기능을 가지고 있는 것에 비해서 상대적으로 Documentation은 그다지 친절하지 않다는 점이다. 각 클래스 내부에 있는 함수들이 어떤 역할을 하는지, 설명이 부족한 경우가 많고, 특히나 돌아가는 pipeline을 좀 고치고 싶어서 코드를 보자니 매우 복잡한 구조로 되어 있다. 하루종일 삽질(?)하며 얻은 abbreviation 추가 방법등을 공유하고자 포스팅을 진행하게 되었다.

 

 

nltk.sent_tokenize로 문장 나누기


 

import nltk
load_file=open('입력 파일 경로','r')
save_file=open('저장할 파일 경로','w')
no_blank = False
while True:
    line = load_file.readline()
    if line == "":
        break
    if line.strip() == "":
        if no_blank:
            continue
        save_file.write(f"{line}")
    else:
    	#line sample : 'Hello World. This is sample line."
        result_ = nltk.sent_tokenize(line)
        #result_ : ['Hello World.', 'This is sample line.']
        result  = [ f"{cur_line}\n" for cur_line in result_ ]
        for save_line in result:
            save_file.write(save_line)
        # 문장별로 개행된 파일 저장

 

nltk.sent_tokenize를 사용할 경우, punkt 모델을 활용하여 sentence tokenization을 진행하게 된다. punkt 또한 문장 구조를 학습한 일종의 모델로, 어떤 것이 약어에 쓰이는 "."이고(Ex : Ph.D.), 어떤 것이 마침표인지 학습이 되어있다. 문장을 기본적으로 마침표를 기준으로 나누되, Ph.D., Saint., Professor., 와 같은 약어(Abbreviation)는 Known abbreviation으로 학습하여 한 단어로 취급하는 방식이다.

 

하지만 이러한 punkt모델에도 치명적인 단점이 있는데, 모든 약어를 학습하지 못했다보니, Vol. 13, Apr. 13 과 같은 표현 및 U.S. Pat. No. 134 과 같은 복잡한 약어는 Known abbreviation이 아니여서 모두 나눠져버린다는 것이다.

"Vol. 13" -> ['Vol.', '13'] "Apr. 13" -> ['Apr.', '13'] "U.S. Pat. No. 134" -> ['U.S.', 'Pat.', 'No.','134']

 

그래서, 위와 같은 사태를 방지하기 위해, 내 데이터에 맞는 Known Abbreviation을 추가해주어야 한다.

하지만 punkt 모델을 새로 정의하고 나의 Known Abbreviation을 추가할 경우, 기존에 학습되어있던 약어들을 일일히 개발자가 직접 입력해줘야하는 부작용이 있다.

 

오랜 삽질 끝에, 코드 분석과 스택오버플로우를 통해 답을 찾을 수 있었다.

 

nltk.sent_tokenize에 Abbreviations 추가하기


import nltk
from nltk.data import load
tokenizer = load("tokenizers/punkt/english.pickle")

 

코드를 분석해 본 결과, nltk.sent_tokenizer는 nltk_data cache폴더에 있는 tokenizers/punkt/english.pickle 파일을 불러온다는 것을 알게 되었다. 즉 일단, 먼저 해줘야 하는 것은 tokenizer를 직접 불러온 뒤 이 tokenizer를 뜯어 고쳐야 한다는 것이다.

 

extra_abbreviations = [
    'RE','re','pat', 'no', 'nos','vol','jan','feb','mar','apr','jun',
    'jul','aug','sep','oct','nov','dec','eng','ser','ind','ed','pp',
    'e.g','al','T.E.N.S', 'E.M.S','F.E','U.H.T.S.T','degree',
    '/gm','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
    'P','Q','R','S','T','U','V','W','X','Y','Z']
tokenizer._params.abbrev_types.update(extra_abbreviations)

 

문제는 load로 불러온 Tokenizer는 punktTokenizer의 메인 클래스의 인스턴스가 아니라 이미 학습된 PunktTokenizer을 활용한 SentenceTokenizer라는 클래스의 인스턴스라는 점이다. 

여기서 막혀서 고생을 했지만, 모든 해답을 알고 계시는 스택오버플로우의 도움을 받아, _params로 파라미터에 직접 접근 한 뒤, abbrev_types을 강제로 업데이트하면 된다는 사실을 알게 되었다.

 

extra_abbreviations를 선언한뒤, 원하는 약어들을 넣으면 되는데, punkt는 기본적으로 "글자 전부가 대문자인 단어" 외에는 모두 소문자 단어로 치환하여 처리한다. 예를 들어 "B.E.S.T"는 모두 대문자이므로, abbreviations에 추가할 때 "B.E.S.T"로 입력하면 되지만, Jan, Vol, E.g. 등은 모든 문자를 소문자로 바꿔서 'jan', 'vol', 'e.g'로 추가해야 한다.

또한, 추가하는 방법은 약어를 상징하는 마침표 앞부분까지만 입력을 해야한다.

예를 들어 No. 137이 쪼개지지 않게 하기 위해서는 abbreviation으로 'no.'가 아니라 'no'만 추가하면 된다.

 

 

튜닝된 Tokenizer를 통해 다시 Sentence 분리하기


 

import nltk

from nltk.data import load
tokenizer = load("tokenizers/punkt/english.pickle")
extra_abbreviations = [
    'RE','re','pat', 'no', 'nos','vol','jan','feb','mar','apr','jun',
    'jul','aug','sep','oct','nov','dec','eng','ser','ind','ed','pp',
    'e.g','al','T.E.N.S', 'E.M.S','F.E','U.H.T.S.T','degree',
    '/gm','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
    'P','Q','R','S','T','U','V','W','X','Y','Z']
tokenizer._params.abbrev_types.update(extra_abbreviations)

load_file=open('./input.txt','r')
save_file=open('./output.txt','w')
no_blank = False
while True:
    line = load_file.readline()
    if line == "":
        break
    if line.strip() == "":
        if no_blank:
            continue
        save_file.write(f"{line}")
    else:
        print(line)
        result_ = tokenizer.tokenize(line)
        print(result_)
        result  = [ f"{cur_line}\n" for cur_line in result_ ]
        for save_line in result:
            save_file.write(save_line)

 

 

 

위와 같은 방식을 사용하면, 위와 같은 복잡한 Corpus 파일이 문장 별로 개행된 파일로 아래와 같이 변경된다.

 

 

 

많은 블로거 분들이 sent_tokenize를 다뤘지만, abbreviation을 추가하는 방법에 대해서는 다루고 있는 글이 잘 없었다.

뿐만 아니라, 아무리 찾아봐도 NLTK의 공식 문서에도 해당 내용은 찾아볼 수가 없어서 정말 하룻밤을 꼴딱 새며 해결해야했다. 이 글을 보시는 연구자분들은 삽질 없이 능률적인 개발을 하실 수 있기를 바래본다.

반응형
블로그 이미지

Hyunsoo Luke HA

석사를 마치고 현재는 Upstage에서 전문연구요원으로 활동중인 AI 개발자의 삽질 일지입니다! 이해한 내용을 정리하는 용도로 만들었으니, 틀린 내용이 있으면 자유롭게 의견 남겨주세요!

,
반응형


 

자체 데이터셋으로 BertTokenizer 학습하기 

 

이번 게시글에서는 Pretrained Weight를 이용하지 않고, 특정 Domain에 맞도록 직접 Custom Dataset을 통해 BertTokenizer를 학습시키는 방법을 다룬다.

 

현재 진행하고 있는 프로젝트에서, 특정 분야에 대해서 처음부터 다시 학습된 (Train from Scratch) BERT 모델을 구현할 필요가 있어서 많은 시행착오를 가지며, 포스팅을 진행하였다.

 

라이브러리로는 가장 편리성이 좋은 huggingface를 사용한다.huggingface는 transformer, tokenizers, datasets 와 같이 다양한 라이브러리의 집합체로, 서로 호환성이 좋기 때문에 쉽게 모델을 학습시킬 수 있다는 장점이 있다. 다만, 공식 Documentation에 최신 버전에 맞지 않는 예전 내용이 담겨있는 경우가 있거나, 비슷한 기능을 하는 수 많은 function(save, save_model, save_pretrained 등등)들이 서로 뭐가 다른지 직관적이지 않은 이름을 가지고 있어서 매우 혼란스러운탓에 혼자서 개발하기에 어려움이 있었다. 부디 이 글이 NLP를 시도해보고자하는 학생분들과 개발자분들에게 도움이 되었으면 한다.

 

BertTokenizer 학습부터 시작해서, TPU 기반의 학습 방법까지 모든 내용을 향후 포스팅을 통해 다루고자 한다.

 

Requirements


 

pip install pathlib
pip install tokenizers

 

먼저 huggingface의 tokenizers를 pip를 통해 다운받는다.

자세한 설명은 아래 링크의 Readme를 통해 참고할 수 있다.

https://github.com/huggingface/tokenizers

 

GitHub - huggingface/tokenizers: 💥 Fast State-of-the-Art Tokenizers optimized for Research and Production

💥 Fast State-of-the-Art Tokenizers optimized for Research and Production - GitHub - huggingface/tokenizers: 💥 Fast State-of-the-Art Tokenizers optimized for Research and Production

github.com

 

 

Parameters


 

먼저 tokenizer config을 custom dataset에 맞춰서 진행해주어야 한다.

parameter를 적절하게 고르는것이 어려운데, 이럴 땐 naive하게 기존에 유명한 모델들의 값을 참고하면 좋다.

어차피 vocab_size등은 데이터셋에 따라서 엄청 크게 요동치지는 않는 것으로 보이며, 대부분의 모델들이 30000~35000사이로 많이 사용하고 있다. 

 

vocab_size를 아무리 크게 잡아도, 학습 데이터 수가 충분하지 않으면, vocab_size보다 작은 vocab이 최종적으로 도출될 수 있으므로, 무조건 크게 하는 것이 좋지는 않다는 것을 명심하자.

 

my_vocab_size = 32000
# vocab의 크기를 의미한다. 적을 수록 "단어" 단위로, 클 수록 "음절" 단위로 나뉘어진다.
my_limit_alphabet = 6000
# 모든 알파벳을 커버할 수 있도록 하여, [UNK] 빈도를 줄이기 위해 6000을 선택했다.
my_special_tokens = ["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"]
# tokenizer 에서 사용될 special_tokens이다. 필수 토큰은 위와 같다.
user_defined_symbols = ['[BOS]','[EOS]']
# 이제부터는 부가적인 토큰이다. 문장의 시작과 끝을 알리는 토큰을 추가했다.
unused_token_num = 200
unused_list = ['[unused{}]'.format(n) for n in range(unused_token_num)]
# KoELECTRA Github를 참고하여, unused 토큰을 약 200개 추가했다. 범용성을 높일 수 있다.
user_defined_symbols = user_defined_symbols + unused_list
my_special_tokens = my_special_tokens + user_defined_symbols

 

 

Training


위에서 설정한 내용을 바탕으로 학습을 진행한다.

Corpus들은 알맞게 전처리를 하여 txt형태로 넣으면 되는데,

일반적으로 한 줄에 한 문장씩만 배치하는 방식으로 깔끔하게 쪼개 넣으면 된다.

nltk를 사용하여 Sentence 분리하는 방법도 곧 포스팅 할 예정이다.

 

paths = [str(x) for x in Path("./data/train").glob("*.txt")]
# 학습에 사용될 Corpus들을 넣으면 된다. 

tokenizer = BertWordPieceTokenizer(
    clean_text=True,
    handle_chinese_chars=True,
    strip_accents=True, 
    # 만약 cased model이라면 반드시 False로 해야한다, 또한 한글의 경우 cased model로 하면 글자가 자소분리된다.
    lowercase=True,
    # 대소문자 구분 여부를 의미한다. 한글의 경우 무의미하므로 신경쓰지 않아도 된다.
    wordpieces_prefix="##"
)

tokenizer.train(
    files=paths,
    limit_alphabet=my_limit_alphabet,
    vocab_size=my_vocab_size,
    min_frequency=5,
    # pair가 5회이상 등장할시에만 학습
    show_progress=True,
    # 진행과정 출력 여부
    special_tokens=my_special_tokens
)

 

Saving


 

Saving 방법이 매우 다양해서 혼란스러웠는데, 최종적으로 아래 2가지 방법 중 편리한 것을 선택하면 된다.

 

1. vocab만 추출하는 방식

 

tokenizer.save("./tokenizer/tok_added-ch-{}-wpm-{}".format(my_limit_alphabet, my_vocab_size),True)
# config, model등 저장하지 않고 vocab 정보만 json 형태로 저장



import json
vocab_path = "your tokenizer path"
# save의 결과로 추출된 파일 경로
vocab_file = './tokenizer/vocab.txt'
# vocab.txt 형태로 저장할 경로
f = open(vocab_file,'w',encoding='utf-8')
with open(vocab_path) as json_file:
    json_data = json.load(json_file)
    for item in json_data["model"]["vocab"].keys():
        f.write(item+'\n')

    f.close()

 

위와 같은 방식으로 저장했을 시, 아래와 같이 vocab 목록만 저장된 txt파일이 최종적으로 저장되며, 이를 통해 추후 tokenizer를 불러올 수 있다.

 

 

2. pretrained model 형식으로 저장하기

 

huggingface는 자체적으로 pretrained model을 불러올 수 있도록 규격화된 모델 디렉터리 형식이 존재한다.

아래 코드를 통해 tokenizer를 pretrained_model 형태로 저장한다. 1번 방식과 달리 vocab.txt, tokenizer_config.json, special_tokens_map.json 3개의 파일이 저장된다.

 

Tokenizer를 활용하여 추후 자체 NLP 모델까지 학습을 진행하고자 한다면, pretrained model형식으로 저장해두는것이 나중에 학습용 스크립트에서 AutoTokenizer를 통해 불러올 때 용이하므로, 이 방식을 추천한다.

 

문제는 BertWordPieceTokenizer 클래스는 save_pretrained 파라미터가 없다는 것이다.

아마 tokenizers와 transformers 라이브러리의 차이가 아닐까 싶다.

 

따라서, 위에서 학습된 vocab.txt파일을 먼저 BertTokenizer 형태로 불러온 후,

save_pretrained를 진행할 수 있다.

애초부터 BertTokenizer로 학습이 가능한지는 시도를 못해봤다. (혹시 아시는 분 있으시면 댓글 부탁드립니다!)

 

from transformers import BertTokenizer
vocab_path = "<vocab.txt 경로>"
tokenizer = BertTokenizer(vocab_file=vocab_path, do_lower_case=True) 
tokenizer.save_pretrained('./<저장하고 싶은 경로>/')

 

 

Loading


저장한 방법 두가지에 따라서 불러오는 방법도 다르다.

 

1. Vocab 파일을 통해 불러오는 방식

 

from transformers import BertTokenizer
vocab_path = "<vocab.txt 경로>"
tokenizer = BertTokenizer(vocab_file=vocab_path, do_lower_case=True) 
# 학습할 때 썻던 config값을 바탕으로 do_lower_case 여부를 선택해야함

test_str = 'This invention relates to improved metal-containing spinel compositions, particularly for use in a manner to effect a reduction in the emission of sulfur oxides and/or nitrogen oxides to the atmosphere.'
print('테스트 문장: ',test_str)
encoded_str = tokenizer.encode(test_str,add_special_tokens=True)
print('문장 인코딩: ',encoded_str)
decoded_str = tokenizer.decode(encoded_str)
print('문장 디코딩: ',decoded_str)

## 실행 결과 ##
# 테스트 문장:  This invention relates to improved metal-containing spinel compositions, particularly for use in a manner to effect a reduction in the emission of sulfur oxides and/or nitrogen oxides to the atmosphere.
# 문장 인코딩:  [2, 831, 730, 1265, 639, 2081, 1350, 17, 1601, 22544, 2344, 16, 1447, 659, 1038, 625, 43, 2047, 639, 1645, 43, 2809, 625, 618, 3448, 630, 4334, 5878, 641, 19, 661, 3377, 5878, 639, 618, 5376, 18, 3]
# 문장 디코딩:  [CLS] this invention relates to improved metal - containing spinel compositions, particularly for use in a manner to effect a reduction in the emission of sulfur oxides and / or nitrogen oxides to the atmosphere. [SEP]

 

2. AutoTokenizer를 통해 pretrained model을 불러오는 방식

 

tokenizer = AutoTokenizer.from_pretrained('저장된 경로')

 

반응형
블로그 이미지

Hyunsoo Luke HA

석사를 마치고 현재는 Upstage에서 전문연구요원으로 활동중인 AI 개발자의 삽질 일지입니다! 이해한 내용을 정리하는 용도로 만들었으니, 틀린 내용이 있으면 자유롭게 의견 남겨주세요!

,
반응형


 

C++ String 대문자, 소문자화 하는 방법

 

 

C++ transform 함수

 

  • algorithm 헤더 파일에 정의되어 있다.
  • 다양한 방식으로 컨테이너에 대해 변환을 진행해주는 편리한 함수이다.
  • 원형 : transform(begin, end, d_begin, unary_op)로 구성되어있으며, 입력 범위 begin~ end, 그리고 변환 결과를 저장할 위치인 d_begin, 어떤 변환을 할지를 결정하는 unary_op을 입력하면된다.

 

transform으로 string 대문자화, 소문자화 하는 방법

 

string str;
cin >> str;
//소문자화
transform(str.begin(), str.end(), str.begin(), ::tolower);
// 저장할 위치에 str.begin()을 다시 넣음으로써 그대로 결과를 str에 저장함


//대문자화
transform(str.begin(), str.end(), str.begin(0, ::toupper);

 

반응형
블로그 이미지

Hyunsoo Luke HA

석사를 마치고 현재는 Upstage에서 전문연구요원으로 활동중인 AI 개발자의 삽질 일지입니다! 이해한 내용을 정리하는 용도로 만들었으니, 틀린 내용이 있으면 자유롭게 의견 남겨주세요!

,
반응형


 

String remove, erase 사용하여 공백 제거 하는 방법

 

remove 함수

 

  • std::remove 함수는 algorithm 헤더파일에 포함되어 있다.
  • 함수 원형 : std::remove(begin, end, value for delete)
  • 가장 큰 특징은, 일반적인 remove 종류의 함수들과 달리, std::remove는 원소 값 삭제를 하는 것은 맞으나, 실제로 컨테이너 사이즈가 줄어들지가 않는 이슈가 있다.
  • 예를 들어 아래와 같은 현상이 발생한다.
string str = "bE au T I fu L"
remove(str.begin(), str.end(), ' ');
cout << str;

// 결과 값 : bEauTIfuL fu L

 

삭제가 되긴 하였으나, 삭제된 공간만큼 앞으로 당겨지고, string의 길이가 짧아져야 하는데,

앞쪽에는 공백이 제거된 문자열이 잘 정렬되어있지만, 뒤쪽에 기존 string 값인 fu L의 잔재가 남아있다.

 

 

erase 함수

 

string str = "bE au T I fu L"
str.erase(remove(str.begin(), str.end(), ' '), str.end());
cout << str;

// 결과 : bEauTIfuL

 

따라서 위와 같은 방법으로 erase와 함께 결합하여 정상적인 실행 결과를 확인할 수 있다.

반응형
블로그 이미지

Hyunsoo Luke HA

석사를 마치고 현재는 Upstage에서 전문연구요원으로 활동중인 AI 개발자의 삽질 일지입니다! 이해한 내용을 정리하는 용도로 만들었으니, 틀린 내용이 있으면 자유롭게 의견 남겨주세요!

,
반응형


 

String 공백 포함 입력 받는 방법

 

  • string과 char[] 자료형 모두 공백이 포함된 입력을 받았을 때, 공백을 기준으로 다른 인자라고 판단하여, 공백 문자가 나오기 이전까지의 문자들만 입력이 되는 문제가 있다.
  • 이를 해결하기 위해서는  cin이나 scanf가 아닌 getline을 통해 값을 받아와야 한다.

 

string str;
getline(cin, str);

 

getline 사용 유의점


  • 기본적으로 cin으로 입력받을 경우, 변수에 '\n'을 저장하지 않으므로 입력 버퍼에 '\n' 찌꺼기가 남게 된다.
  • cin으로 입력받은 후, 또 cin을 활용하여 입력을 받을 때는 이전 버퍼에 있던 '\n'을 무시하고 또 다시 개행문자를 무시하므로 버퍼를 비우지 않아도 정상작동한다.
  • 하지만 cin으로 입력받은 후, 버퍼에 '\n'가 차있는 상태에서 getline을 사용할 경우, '\n'문자로 인해 정상적으로 입력을 받지 못하는 이슈가 있다.
  • 따라서 버퍼를 cin.ignore()을 통해 비워주어야 한다.
  • getline 이후 getline을 사용할 경우엔 변수에 '\n'가 들어가 있기 때문에 버퍼에는 '\n'가 존재하지는 않으므로 문제가 발생하지 않는다. 초보자들이 많이 실수하는 부분이 getline은 반드시 ignore를 해주어야 한다고 착각하는경우인데, '\n'이 존재하지 않는 버퍼를 강제로 ignore로 비울 경우, 맨 앞 문자 1개가 누락된 입력을 받게 된다. 

 

// cin.ignore가 필요한 경우

cin >> N;
string s;
cin.ignore();
getline(cin,s);

// cin.ignore가 필요하지 않은 경우

cin >> N;
cin >> str;
// (버퍼에 남아있는 '\n'을 자동으로 cin이 무시함)

getline(cin,s);
getline(cin,s2);
// ('\n'을 포함하여 변수 s, s2에 저장하므로 버퍼에 '\n'자체가 남아 있을 이유가 없음
반응형
블로그 이미지

Hyunsoo Luke HA

석사를 마치고 현재는 Upstage에서 전문연구요원으로 활동중인 AI 개발자의 삽질 일지입니다! 이해한 내용을 정리하는 용도로 만들었으니, 틀린 내용이 있으면 자유롭게 의견 남겨주세요!

,
반응형


 

[CLion] C++ 프로젝트 내에서 파일 별로 컴파일 및 실행하기(알고리즘 풀 때 유용!)

 

국내에서는 인기가 없고 잘 안쓰이는 CLion, 디버그나 여러가지 기능이 나름 준수하고,

가벼운 vscode보다 무거운 대신 확실히 디버깅할 때 편해서 코딩테스트에서 뭐가 문제인지 알아보기 편해서 사용한다.

물론 Jetbrain Student License로 학교 이메일 인증하면 무료인점도 한 몫한다.

근데 문제는 CLion 자체가 프로젝트 용으로 출시된 IDE이다 보니, 반드시 프로젝트 단위로만 실행이 가능하며, 한 폴더 안에 여러개의 main 함수가 존재하면 에러가 발생한다.

 

알고리즘 문제를 풀 때, 문제 하나당 한개의 프로젝트 파일을 만들기엔 너무 귀찮으므로, 파일 별로 컴파일 및 실행을 간단히 하는 방법에 대해 알아보던중, 해외 스택 오버플로우에서 답을 얻었다.

 

 

New Executable C/C++ Plugin


https://plugins.jetbrains.com/plugin/12078-new-executable-c-c-

 

New Executable C/C++ - Plugins | JetBrains

Shift + Alt + E on a source file to create an executable with the corresponding file.

plugins.jetbrains.com

 

위 링크를 통해 플러그인을 설치받을 수 있다. 만약 CLion이 로컬에 깔려 있다면 자동으로 인식되어 Install to CLion 이라는 버튼이 보일 것이다.

 

 

 

 

이 버튼을 클릭하게 되면, CLion에 자동으로 설치가 진행이 된다.

사용법을 보게 되면 따로 실행하고 싶은 파일에서 Shift + Alt + E를 누르라고 되어 있다.

 

 

Shift+Alt+E를 클하면 자동으로 CMakeLists.txt에 add_executable이 추가되며 각각의 파일에 대한 컴파일 및 실행이 가능해진다.

코딩 테스트 풀 때 매우 유용하게 사용될 수 있다.

 

만약 main이 여러번 정의되어있다고 에러가 나는 경우에는, 필자처럼 기본적으로 작성되어있는 add_executable을 모두 주석처리하고 원하는 파일에만 Shift+Alt+E를 통해 add_executable을 추가한 후, 컴파일과 실행을 진행하면 정상적으로 분할 실행 된다.

반응형

'각종 Tips > 기타' 카테고리의 다른 글

[한글 / hwp] 표 합치는 방법  (0) 2020.09.15
[Putty] putty inactive 해결법  (0) 2020.08.14
블로그 이미지

Hyunsoo Luke HA

석사를 마치고 현재는 Upstage에서 전문연구요원으로 활동중인 AI 개발자의 삽질 일지입니다! 이해한 내용을 정리하는 용도로 만들었으니, 틀린 내용이 있으면 자유롭게 의견 남겨주세요!

,
반응형


Python에서 예외 발생 시 에러 추적 (Traceback) 하는 방법

 

나는 인공지능을 다루지만, 사실 파이썬에 엄청나게 익숙하지는 않다.

원래는 블록체인을 하면서 C++ 위주로 하다가, 급격히 딥러닝으로 방향을 전환하면서 python으로 프로젝트를 진행하게 되는데, 검색과 구글링을 통해 개발하는 것에 익숙하여 필요한 기능은 다 구현하고 있지만, 의외로 간단한 것을 모르는 경우가 많다.

 

예를 들어, 일반적인 Python 코드에서 에러가 발생할 경우, 자동으로  call stack을 보여주며 정확히 어떤 부분에서 에러가 났는지 추적이 가능하지만, try except 구문을 사용할 경우 자동으로 에러 추적은 보여주지 않게 되는데,

이를 표시하기 위한 방법에 대한 포스팅을 진행한다.

 

 

import traceback

try:
	{ something }
except:
	traceback.print_exc()

 

위와 같은 코드를 통해 임의로 Trackback을 호출할 수 있다.

반응형
블로그 이미지

Hyunsoo Luke HA

석사를 마치고 현재는 Upstage에서 전문연구요원으로 활동중인 AI 개발자의 삽질 일지입니다! 이해한 내용을 정리하는 용도로 만들었으니, 틀린 내용이 있으면 자유롭게 의견 남겨주세요!

,
반응형


 

맥 OS 팀뷰어 종료 후 재시작 하는 방법

 

필자의 경우에는 연구실에 맥북을 클램쉘 모드로 세팅해놓고, 외부에서 원격 접속을 사용하여 작업을 진행한다.

대부분은 ssh으로 진행하지만, 가끔씩은 GUI를 사용할 수 밖에 없는 상황이 있다.

VNC Viewer는 해상도나 화면 분할 등 여러가지 면에서 개인적으로 팀뷰어보다 조금 불편하다고 생각해서 팀뷰어를 주로 사용하는데, 팀뷰어의 경우 매우 빈번하게 접속 이슈가 생기곤 한다.

 

 

특히 이 지긋지긋한 디스플레이 파라미터 초기화 중에서 멈춤 현상이 자주 발생하는데,

자세한 이유는 모르겠으나, VNC Viewer까지 감시모드로 접속이 가능한걸 보면, 완전 접근 상태로 원격 접속이 형성이 된 후에, Connection이 제대로 종료되지 않고 맥북측에서는 계속 원격 제어가 되고 있는 걸로 처리되면서 문제가 생기는 것으로 보인다.

 

이를 해결하기 위해서는 팀뷰어를 종료하고 다시 키면 되는데,

보통 저 상태가 되면 VNC Viewer든 TeamViewer든 모두 먹통이 되어 GUI 환경을 이용할 수 없으므로,

SSH를 통해 프로세스 종료 후 다시 실행을 시켜주면 된다.

 

$ps -acx | 'TeamViewer'

76715 ??         0:49.29 TeamViewer_Desktop
76747 ??         0:13.04 TeamViewer_Service
76834 ??         0:07.26 TeamViewer

 

모든 팀뷰어 관련 프로세스를 종료해준다.

 

$sudo kill -9 76715
$sudo kill -9 76747
$sudo kill -9 76834

 

그 후, 다시 팀뷰어 데몬을 실행시켜준다.

일반적으로 팀뷰어는 /Applications 안에 깔려 있으며, 설치 폴더를 별도로 변경하지 않았다면, 아래 커맨드를 통해 다시 실행시킬 수 있다.

 

$/Applications/TeamViewer.app/Contents/MacOS/TeamViewer
$/Applications/TeamViewer.app/Contents/MacOS/TeamViewer_Desktop
$/Applications/TeamViewer.app/Contents/MacOS/TeamViewer_Service

 

위 세개의 파일을 직접 실행시켜주면 TeamViewer가 작동된다.

만약 실행이 안된다면, sudo 권한으로 시도하거나, chmod a+x을 통해 실행 권한을 확보한 후 실행한다.

 

반응형
블로그 이미지

Hyunsoo Luke HA

석사를 마치고 현재는 Upstage에서 전문연구요원으로 활동중인 AI 개발자의 삽질 일지입니다! 이해한 내용을 정리하는 용도로 만들었으니, 틀린 내용이 있으면 자유롭게 의견 남겨주세요!

,
반응형


맥에서 터미널로 프로세스/앱 강제 종료하는 법

 

맥북이나 맥 OS를 사용하다보면, SSH 등을 통해 터미널로 프로세스나 앱을 종료해야 되는 경우가 종종 생기게 된다.

이 때, 터미널에서 실행되고 있는 프로세스 외에, GUI 기반 앱도 터미널을 통해 종료할 수 있다.

 

$ ps -acx

 

위와 같은 명령어를 통해 

 

 9303 ??         4:45.98 Notion Helper
 9306 ??         0:00.09 AssetCacheTetheratorService
 9310 ??        11:53.60 Atom Helper (Renderer)
 9315 ??         0:00.10 installcoordinationd
 9316 ??         0:03.91 Notion Helper (Renderer)
 9319 ??         0:05.97 commerce
 9321 ??         0:00.21 CacheDeleteExtension
 9322 ??         0:03.48 distnoted
 9324 ??         0:00.17 MusicCacheExtension
 9325 ??         0:00.14 SafariLaunchAgent
 9328 ??         0:00.16 TVCacheExtension
 9330 ??         0:16.83 amsengagementd
 9331 ??         0:27.05 Google Chrome Helper (Renderer)
 9332 ??         0:00.01 SafeEjectGPUAgent
 9333 ??         0:00.05 SafeEjectGPUService
 9353 ??         0:15.79 RdrCEF
 9354 ??         0:56.09 CalendarWidgetExtension
 9356 ??         0:08.84 AppleSpell
 9358 ??        39:45.91 Notion Helper (Renderer)
 9360 ??         0:02.00 Notion Helper (Renderer)

위와 같은 결과를 얻을 수 있다.

터미널에서 실행되고 있지 않은, 노션이나 아톰등과 같은 프로세스까지도 조회가 가능하며, 일반적인 터미널 프로세스와 같이 kill 명령을 통해 종료가 가능하다.

 

$ps -acx | grep 'TeamViewer'

76715 ??         0:49.29 TeamViewer_Desktop
76747 ??         0:13.04 TeamViewer_Service
76834 ??         0:07.26 TeamViewer

 

위와 같이 grep 명령어를 사용하여 특정 프로세스를 찾아 종료할 수 있다.

 

$kill -9  76715
반응형
블로그 이미지

Hyunsoo Luke HA

석사를 마치고 현재는 Upstage에서 전문연구요원으로 활동중인 AI 개발자의 삽질 일지입니다! 이해한 내용을 정리하는 용도로 만들었으니, 틀린 내용이 있으면 자유롭게 의견 남겨주세요!

,
반응형


 

폴더 내에 모든 압축파일 해제하기, unzip * 안되는 이유

 

우리는 기본적으로 모든 파일을 옮기거나 복사할 때는 아래와 같이 * 을 주로 사용한다.

하지만, unzip에서는 이러한 명령어를 지원하지 않는다.

 

cp *.zip ../
# 모든 압축 파일을 상위 디렉토리로 복사한다.
mv *.zip ../
# 모든 압축 파일을 상위 디렉토리로 이동한다.
unzip *.zip 
# 동작하지 않는다.

 

이는 bash쉘 자체가 Wildcard Characters(*, ?, [])등을 인식하여 자동으로 변환한 파라미터를 전달하기 때문이다.

즉 실제 unzip은 아래와 같이 작동하게 된다.

 

# 입력한 명령어 
unzip *.zip

# 실제 처리되는 명령어
unzip a.zip b.zip c.zip d.zip

# a.zip 안에 있는 b.zip 안에 있는 c.zip 안에 있는 d.zip을 압축해제하라.

 

위와 같은 이유로 not matched와 같은 에러 메시지가 발생하게 되는 것이다.

하지만, unzip 내부에서도 이런 Wildcard Expression을 지원하기 때문에, unzip의 파라미터로 직접 *.zip을 전달하면 원하는 결과를 얻어낼 수 있다.

 

 

unzip \*.zip

 

탈출문자를 활용하여 위와 같이 명령어를 입력할 경우, Wildcard Expression이 unzip에 직접 전달되고, 사용자가 의도한대로 unzip은 각각의 압축파일을 해제하게 된다.

반응형
블로그 이미지

Hyunsoo Luke HA

석사를 마치고 현재는 Upstage에서 전문연구요원으로 활동중인 AI 개발자의 삽질 일지입니다! 이해한 내용을 정리하는 용도로 만들었으니, 틀린 내용이 있으면 자유롭게 의견 남겨주세요!

,