오늘은 인강으로 배운 인경 신경망에 대해 정리한다.

인공신경망은 사용자가 다양하게 만들 수 있는 장점을 가지고 있다.

  1. 입력층 : 어떤 값이 처음 들어오는 층
    • x1, x2
  2. 은닉층 : 입력층과 출력층 사이에 있는 층으로, 여러 개의 층이 존재할 수 있음
    • h1, h2
  3. 출력층 : 가장 마지막에 있는 층으로, 최종 값인 ^y(y_Hat) 값을 출력하는 층
    • ^y(y_Hat)
  4. bias : 계산 과정 중에 어떤 값을 더해서 구하고 싶을 때 사용
    • b1, b2, b3

 

  • 입력층에서 은닉층으로 값이 넘어갈 때 연산을 하는데 그 층 사이에 있는 값을 가중치라고 부름
  • 입력층과 은닉충 사이의 가중치는 4개(w1, w2, w3, w4)
  • 은닉충과 출력층 사이의 가중치는 2개(v1, v2)

 

  • 이미지에서 보이는 원은 각각의 노드를 의미하며, 층은 노드들의 모임이라고 볼 수 있음
  • 노드의 개수를 정해줄 수 있음
  • 가중치가 커지면 커질수록 계산하는데 시간이 오래 걸림

 

 

출처 : 인프런 - 실전 인공지능으로 이어지는 딥러닝 개념 잡기(딥러닝 호형)

인프런 - "실전 인공지능으로 이어지는 딥러닝 개념 잡기" 강의 들으며 정리한 내용입니다.

인공신경망 왜 배워야 해?

  • 인공지능 기술의 핵심이라서 배워야 해
    • feature learning 개념을 가지고 있음
  • 패러다임이 변화했기 때문에 배워야 해
  • 다양한 분야에 적용 가능하기 때문에 배워야 해

 

딥러닝은 무엇인가?

  • 인간의 신경망을 모사하여 만든 인공 신경망을 깊게 만들어 모델을 학습시키는 방법
  • 사람처럼 생각하고 행동할 수 있도록 인간의 학습능력, 이해능력 등을 프로그래밍을 통해 구현한 지능
  • 따라서, 기계도 사람처럼 생각할 수 있는가? 라는 본질적인 질문에서 시작됨
  • 머신러닝? 인공지능을 구현하기 위한 하나의 기술
  • 머신러닝 안에 다양한 모델이 존재

 

 

 

출처 : 인프런 - 실전 인공지능으로 이어지는 딥러닝 개념 잡기(딥러닝 호형)

아무것도 모르는 나란인간.. BERT가 뭔지 알아보기로 했다..

BERT란?

  • Bidirectional Encoder Representations from Transformers
  • Transformer를 Bidirection한 Encoder
    • Bidirection : 양방향
    • Encoder : 입력 값을 숫자 형태로 바꾸는 모듈
  • BERT는 문맥을 양방향을 위해 숫자의 형태로 바꿔주는 딥러닝 모델
  • BERT는 Language Model
  • Pre-training 과 Fine-tuning 두 파트로 나뉘어짐
  • GPT1과 동일한 사이즈의 BERT는 GPT1 보다 높은 성능을 가짐

 

BERT Pre-Training

  • 입력 토큰들이 Position Embedding과 Segment Embedding과 더해짐
  • CLS : Classification, 분류 Task에 사용되기 위한 벡터, 문장 전체가 하나의 벡터로 표현된 스페셜 토큰
  • SEP : 두 문장으로 구성된 입력값의 경우에 사용되는 스페셜 토큰, 문장을 구분해줌 
    • WordPiece Embedding
      1. WordPiece Embedding을 사용해서 문장을 토큰 단위로 분리함
      2. WordPiece Embedding는 단순히 띄어쓰기로 토큰을 나누는 것보다 효과적으로 토큰을 분리함
    • Segment Embedding
      1. 두 개의 문장이 입력될 경우에 각각의 문장에 서로 다른 숫자를 더해주는 것
      2. 딥러닝 모델에게 두 개의 다른 문장이 있다는 것을 쉽게 알려주기 위해 사용하는 Embedding
    • Position Embedding
      1. 토큰들의 상대적 위치정보를 알려줌
      2. 딥러닝 모델은 Position Embedding으로 e1 다음에 e2, e2 다음에 e3 토큰이 위치함을 알 수 있음
      3. sin, cos 함수를 이용함
        • sin과 cos 출력 값은 입력 값에 따라 달라짐
        • 따라서 sin, cos 출력 값은 입력 값의 상대적인 위치를 알 수 있는 숫자로 사용 가능함
        • sin과 cos 출력 값은 규칙적으로 증가 또는 감소함
        • 따라서, 딥러닝 모델이 이 규칙을 사용해서 입력 값의 상대적 위치를 쉽게 계산 가능함
        • sin과 cos는 무한대 길이의 입력 값도 상대적인 위치를 출력할 수 있음
        • 어떤 위치의 입력 값이라도 -1 에서 1사이의 값을 출력하게 되어 있음
        • 상대적 위치를 사용하는 이유?
          1. 절대적 위치를 사용할 경우 최장 길이 문장을 세팅해야 함
          2. 따라서 최장 길이 문장보다 긴 학습 데이터는 사용할 수 없음

 

BERT Fine Tuning

  • 두 문장의 관계를 예측하기 위해 모델을 만드는 방법
    1. 두 개의 문장을 SEP 토큰으로 구분해서 BERT에 입력하여
    2. 력 값의 첫번째 CLS 토큰이 두 문장의 관계를 나타내도록 학습시킴

  • 문장을 분류하는 모델
    1. 문장 1개를 입력받고 CLS 토큰이 분류 값 중 하나가 되도록 학습시킴

  • QnA 예제(질의 및 응답 예제)
    1. 입력 값으로 질문과 정답이 포함된 장문을 SEP 토큰으로 구분해서 줌 
    2. BERT에 출력 값의 마지막 토큰들이 장문 속에 위치한 정답의 시작 인덱스와 마지막 인덱스를 출력하도록 학습시킴

  • 문장 속 단어를 태깅하는 예제 
    1. 각각의 입력 토큰에 대한 출력 값이 있기 때문에, 이 출력 값이 원하는 태깅으로 출력되도록 학습시킴

 

Transformer란?

  • 구글에서 공개한 Encoder, Decoder 구조를 지닌 딥러닝 모델
  • 기계번역에서 우수한 성능을 보여준 모델
  • Encoder는 입력 값을 양방향으로 처리하고
  • Decoder는 입력 값을 왼쪽에서 오른쪽으로 단방향으로 처리 함

 

Transformer 작동방식

  1. 입력 값은 먼저 Encoder에 입력됨
  2. 입력 값이 Encoder에 입력되면 각 토큰들은 포지셔널 인코딩과 더해지고
  3. 인코더는 이 값들을 행렬 계산을 통해서 한방에 Attention 벡터를 생성함
    • 벡터는 토큰의 의미를 구하기 위해서 사용됨
    • 단어하나만 보면 그 단어의 의미가 상당히 모호할 때가 많음
    • 예를 들어, 문장 전체를 보지 않고 단순히 '텍스트'라는 단어만 보면 텍스트가 신문지 속에 있는 문장들을 의미하는지, 문자메세지를 보내는 행위를 말하는 지 알기 어려움
    • 마찬가지로 메세지라는 단어만 봤을 경우 누가 말로 전하는 소식인지, 문자인지 헷갈림
  4. Attention  벡터는 fully connected layer로 전송됨
  5. 이와 동일한 과정이 6번 진행되어 그 최종 출력 값을 Transformer Decoder로 입력 값으로 사용함
    • Encoder는 모든 토큰을 한방에 계산함
    • 왼쪽에서 오른쪽으로 하나씩 읽어가는 과정이 없음
  6. Decoder는 Encoder의 출력 값과 최초 스타트 스페셜 토큰으로 작업 시작함
  7. Decoder는 왼쪽부터 오른쪽으로 순차적으로 출력 값을 생성함
  8. Decoder는 이전에 생성된 Decoder의 출력 값과 Encoder의 출력 값을 사용해서 현재의 출력 값을 생성함
  9. Decoder 역시 Attention 벡터를 만들고 fully connected layer로 전송하는 작업을 6번 진행함
  10. Decoder는 end토큰이라는 스페셜 토큰을 출력할 때까지 반복함

 

BERT가 양방향 Encoder를 취하게 된 이유?

  • GPT1으로부터 시작됨
    • GPT1이란?
    • 2018년에 openAI 에서 Transformer Decoder 구조를 사용해서 만든 자연어 처리 모델
    • GPT1은 Generative training으로 학습된 Language 모델이 얼마나 자연어 처리 능력이 우수한지 보여준 우수한 모델
    • 기본적으로 문장을 데이터로 사용함
    • 단어를 하나씩 읽어가면서 다음 단어를 예측하는 방법으로 학습됨
    • 이런 학습 방식은 별도의 라벨링 작업이 필요하지 않아 비지도 학습임
    • 한 문장만 가지고도 여러 학습 데이터를 만드는 것이 가능함
    • 현재 위치의 단어 다음에 위치한 단어를 예측하는 방식으로 학습됨
  • 구글은 질의 및 응답 영역은 문맥이해 능력이 상당히 중요한데, 단순히 왼쪽에서 오른쪽으로 읽어나가는 방식으로는 문맥이해의 약점이 있을 수 있다고 지적함
  • 이에 단순히 왼쪽에서 오른쪽으로 읽어나가는 Decoder보다 양방향으로 문맥을 이해할 수 있는 Encoder를 활용한 Language 모델을 BERT라는 이름으로 발표함

 

BERT vs GPT

  • BERT
    1. 양방향 LM
    2. Fine Tuning을 하도록 만들어짐
  •  GPT
    1. 단방향 LM
    2. Fine Tuning을 하지 않도록 만들어짐

 

글에 문제가 있으면 댓글로 알려주시면 감사하겠습니다!

그럼 이만.!

출처

1. https://www.youtube.com/watch?v=30SvdoA6ApE (허민석님의 [딥러닝 자연어처리]BERT이해하기)

2. Jacob Devlin, Ming-Wei Chang, Kenton Lee, Kristina Toutanova, "BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding"(https://arxiv.org/pdf/1810.04805.pdf)

'Study' 카테고리의 다른 글

multiprocessing 겅부..  (0) 2022.09.18
local에서 ssh 서버에 띄운 jupyter notebook 붙기  (0) 2022.02.06
Neural Search 관련 공부  (2) 2021.07.07
머신러닝 개념 정리  (0) 2021.05.22

LegalQA에 대해 알아가는 중에 공부한 내용을 끄적끄적 기록하려고 한다.

LegalQA : https://github.com/haven-jeon/LegalQA

 

1. SentenceKoBART에 기반한 법적 QA시스템

그렇담 SentenceKoBART는 무엇인지 알아야 한다.

 

* SentenceKoBART란?

- Sentence-BERT에 기반하여 KorSTS, KLUE STS Dataset을 사용하여 Fine tuning한 모델이다.

 

Sentence-BERT란 또 무엇인가. 알아야 할 게 너무나 많다 ㅠㅠ

LegalQA 시스템에 대해 알기 위해 Sentence-BERT에 대해 먼저 알아보자.

 

* Sentence-BERT란?

- cosine-similarity 사용하여 비교할 수 있는 의미론적으로 의미있는 문장 임베딩을 도출하기 위해 Siamese and triplet network 구조를 사용하는 pretained BERT 네트워크의 변형이라고 한다....

 

그렇다고 한다.. 자세하게 알아보기 위해 아래에서 구글과 파파고의 도움으로 논문을 열심히 파헤쳐보겠다.

- 논문 전문: https://arxiv.org/pdf/1908.10084.pdf 

 

Introduction


- Siamese과 triplet network 구조를 적용함으로써 기존에 BERT가 하지 못했던 "large-scale semantic similarity comparison, clustering, and information retrieval via semantic search" 태스크에 사용할 수 있었다.

- BERT에 두 개의 문장이 transformer network에 전달되고 target value가 예측된다.

- clustering 및 semantic search 처리하는 일반적인 방법은 의미가 유사한 각 문장을 벡터 공간상에 가깝도록 맵핑하는 것이다.

- BERT에 개별 문장을 입력하고 고정된 크기의 문장 임베딩을 도축하기 시작했는데, 가장 일반적인 방법은 output layer을 평균을 내거나 첫 번째 토큰([CLS] 토큰)의 출력을 사용하는 것이다. 이 방법은 GloVe 임베딩에 평균을 내는 것보다 안좋다.

- 그래서 나온 것이 SBERT(Sentence-BERT)이다.

- siamese network architecture를 사용하면 입력 문장에 대해 고정 된 크기의 벡터를 유도할 수 있다.

- cosine similarity, Manhatten / Euclidean distance와 같은 유사도 측정 방법을 사용하면 의미적으로 유사한 문장을 찾을 수 있다.

- NLI 데이터로 SBERT를 fine tuning 하여 InferSent 및 Universal Sentence Encoder와 같은 SOTA sentence embedding methods보다 성능을 높였다.

 

 

- 공부하는데 참고한 블로그

1) https://velog.io/@ysn003/%EB%85%BC%EB%AC%B8-Sentence-BERT-Sentence-Embeddings-using-Siamese-BERT-Networks2)

 

[논문] Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks

사단법인 한국인공지능연구소에서 진행하는 오픈랩 프로그램이 7기로 참여했고, 어찌어찌 NLP 분야의 팀에서 활동을 하게 되었다. 여기서 BERT 공부를 다 끝내면 한 번 읽어보라고 논문을 추천받

velog.io

2) http://mlgalaxy.blogspot.com/2020/09/sentence-bert-sentence-embeddings-using.html

 

논문 설명 - Sentence-BERT : Sentence Embeddings using Siamese BERT-Networks

  논문 정보 링크 :  https://arxiv.org/abs/1908.10084 EMNLP 2019 Introduction BERT 네트워크에 siamese, triplet 구조를 적용했다. 이로 인해 기존의 BERT가 하지 못했던 large-sc...

mlgalaxy.blogspot.com

 

 

2. Nerual Search Engine인 "Jina" 사용

* Jina란?

- 신경망 기반 검색 애플리케이션을 설계하고 구현하기 위한 오픈소스 신경 검색 프레임워크(open-source neural search framework)!

* Jina에서 사용하는 Flow(FinBERT-QA에서 사용한 예시)

1. Index Flow

출처 : https://towardsdatascience.com/how-to-build-a-production-ready-financial-question-answering-system-with-jina-and-bert-48335103043f

- Index Flow의 기본 아이디어는 사전 훈련된 BERT 모델을 사용하여 모든 answer 구절을 Embedding하여 인코딩한 다음 Query Flow에서 검색할 수 있도록 Embedding 한 것을 Indexing 하는 것이다.

 

1-1. Define our data

- 사용하고자 하는 데이터를 인덱싱하기 위해서는 Jina data type인 Document로 정의 해야 한다.

- app.py 파일에는 검색 애플리케이션으로 구성한다.

- config 함수에서 데이터 경로를 다음과 같이 설정한다.

def config():
	os.environ['JINA_DATA_PATH'] = 'dataset/test.csv'

 

- config 함수 생성 후에 answer 구절에 해당하는 ID와 텍스트를 포함하도록 Document 정의한다.

from jina import Document

def index_generator():
    import csv
    data_path = os.path.join(os.path.dirname(__file__), os.environ['JINA_DATA_PATH'])

    # Get Document and ID
    with open(data_path) as f:
        reader = csv.reader(f, delimiter='\t')
        for data in enumerate(reader):
            d = Document()
            d.tags['id'] = int(data[0])
            d.text = data[1]
            yield d

 

1-2. Endocing our data to embeddings

- Answer Passage를 인코딩한다.

- Jina는 Drivers를 사용하여 Executor용 데이터를 translate 하므로, 익숙한 데이터 유형(예: 텍스트, 이미지, np, 배열 등)만 사용하면 된다.

- 인코딩 단계에서 Driver는 Document를 Byte 단위로 수신하여 Document를 해석하고, Document의 텍스트를 인코더에 전달한다.

- 인코더가 해당 텍스트에 대한 Embedding을 출력한 후, 동일한 Driver가 Embedding을 다시 해석하여 Document에 추가한다.

 

Indexing에 앞서 Pea와 Pod에 관해 알아보자.

더보기

- Pea : Executor는 데이터를 처리할 수 있는 Driver가 필요하기 때문에 둘 다 Flow에서 마이크로 서비스의 필수 구성 요소이다. 따라서 우리는 Encoder Microservice를 얻기 위해 Pea를 사용하여 Executor와 Driver를 함께 wrapping 한다. Pea는 게이트웨이 또는 Flow의 다른 Peas에서 들어오는 메시지를 지속적으로 수신 대기하고 메시지를 수신할 때 Driver를 호출하는 마이크로 서비스이다.

 

- Pod : 신경 검색 애플리케이션을 최적화하기 위해 Jina는 기본적으로 병렬화를 제공한다. 단일 인코더를 사용하는 대신 여러 프로세스로 분할할 수 있다. multiple Encoder microservice가 하나의 Encoder처럼 기능적으로 작동하도록 하기 위해 Peas 그룹을 Pod에 wrapping 한다. Pod는 load balancing, further control, context management를 담당하는 microservice 그룹이다.

- 이 디자인의 장점은 Pod가 로컬 호스트 또는 네트워크를 통해 서로 다른 컴퓨터에서 실행될 때, 애플리케이션을 분산시키고, 효율적이며 확장 가능하다는 것이다.

- Jina가 제공하는 building block을 사용하여 아래 2가지를 할 수 있다.

   1) design an Index Flow

   2) create an Encoder Pod with two simple YAML files   

   

1. Create a Pod for the Encoder

   - 먼저 pods 폴더 안에 encode.yml 파일을 생성한 후, Jina Hub에서 사용할 Encoder이름을 지정한다.

!사용할 인코더 이름
with:
  pool_strategy: auto
  pretrained_model_name_or_path: model/모델이름
  max_length: 512

 

2. Add the Encoder to the Index Flow

  - 위에서 Encoder가 준비되었으므로, Index Flow를 만들어 본다.

  - flows 폴더 안에 index.yml 파일을 생성한 후, Index Flow에서 첫 번째 Pod를 지정한다.

  - 매개변수를 사용하여 Encoder를 분할할 프로세스의 수를 지정할 수 있다.

!Flow
pods:
  encoder:
    uses: pods/encode.yml
    parallel: $JINA_PARALLEL
    timeout_ready: 600000
    read_only: true

 

1-3. Index our answer collection

- answer에 대한 Embedding을 얻은 후 Query time에 검색할 수 있도록 데이터를 저장하기 위해 Indexer라는 또 다른 Executor를 만든다.

- 이전 단계와 유사하게 Driver는 Document를 수신하고 docid, doc 및 Embedding을 Indexer한테 전달한다.

- Jina Hub의 (1)Vector 와 (2)Key-Value Indexer를 모두 사용하여 단일 Indexer 역할을 하는 복합 Indexer을 사용한다.

 

(1)Vector Indexer : k-nearest neighbors algorithm을 사용하여 가장 가까운 answer Embedding을 검색하기 위해 answer Embedding을 저장하고 question Embedding으로 query된다.(뭐라고 해석해야 할 지 모르겠다ㅠㅠ..)

(2)Key-Value(KV) Indexer : Document Data(텍스트, blob, 메타데이터)를 저장하고, docid(일반적으로 벡터 인덱서에서 추출)로 쿼리하여 answer id 및 텍스트와 같은 데이터 정보를 검색한다.

 

- 검색 애플리케이션에서 인덱스 흐름을 사용하는 방법을 살펴보자. app.py에서 config 함수에서 parallel을 변경하여 각 Pod에 대해 각 마이크로 서비스를 분할하려는 Peas(프로세스) 수를 나타낼 수 있다. 인덱싱 단계에서 parallelization를 나타내기 위해 shard를 변경할 수도 있다. 아래 코드에서는 둘 다 변경되지 않은 상태로 둔다. 이것은 각 Pod에 하나의 Pea만 가질 것임을 의미한다.

- Step1. Define our data에서 추가한 index_generator 함수 이후에,  먼저 Flows/index.yml 에서 생성한 Index Flow를 로드하고 index_generator의 입력 Document를 플로우로 전달하는 index 함수를 추가한다. answer 구절을 임베딩으로 인코딩하기 위해 batch_size=16을 설정한다.

from jina.flow import Flow

def index():
    f = Flow.load_config('flows/index.yml')

    with f:
        f.index(input_fn=index_generator, batch_size=16)

 

- Data를 Index할 준비가 되었으니 실행을 시켜보자.

python app.py index

 

Driver와 Indexer를 Pea 안에서 wrapping하고, Peas를 Pod로 그룹화하고, YAML 파일을 사용하여 정의한다.

너무 길어서 숨겨두었으니 "더보기"를 눌러서 좀 더 확인해보쟈.

더보기

1. Create a Pod for the Indexer

- 먼저 pods 폴더 안에 doc.yml 파일을 생성한 후, NumpyIndexer인 Vector Indexer와 BinaryPbIndexer인 KV Indexer로 구성된 복합 Indexer인 CompoundIndexer 를 정의한다.

- 인덱싱된 데이터는 vec.gz와 doc.gz에 각각 저장된다.

- workspace는 인덱스가 저장될 디렉토리이다.

!CompoundIndexer
components:
  - !NumpyIndexer
    with:
      index_filename: vec.gz
    metas:
      name: vecidx
      workspace: $WORKDIR
  - !BinaryPbIndexer
    with:
      index_filename: doc.gz
    metas:
      name: docidx
      workspace: $WORKDIR
metas:
  name: doc_compound_indexer
  workspace: $WORKDIR

 

2. Add the Indexer to the Index Flow

  - flows/index.yml로 돌아가서 Index Flow에 doc_indexer로 Indexer를 추가한다.

  - 만약 데이터가 크다면, 최적화를 위해 애플리케이션에 sharding을 추가할 수 있다.

  - app.py에서 환경 변수로 사용된다.

!Flow
pods:
  encoder:
    uses: pods/encode.yml
    parallel: $JINA_PARALLEL
    timeout_ready: 600000
    read_only: true
  doc_indexer:
    uses: pods/doc.yml
    shards: $JINA_SHARDS
    separated_workspace: true

 

Jina의 Flow API를 사용하여 Index Flow를 시각화 할 수 있다.

from jina.flow import Flow

f = Flow.load_config('flows/index.yml')
f.plot()

 

2. Query Flow

출처 : https://towardsdatascience.com/how-to-build-a-production-ready-financial-question-answering-system-with-jina-and-bert-48335103043f

- 데이터를 인덱싱한 후 Query Flow를 만들어야 한다. Query Flow의 기본 아이디어는 동일한 BERT 기반 모델을 사용하여 주어진 질문을 임베딩으로 인코딩하고 인덱서를 사용하여 가장 유사한 답변 임베딩을 검색하는 것이다. 검색 결과를 더욱 향상시키기 위해 저자의 순위 재지정 기술을 사용한다. 따라서 FinBERT-QA를 사용하여 Jina가 반환한 답변 일치의 점수를 다시 계산하는 또 다른 순위 변경 단계를 추가해야 한다.

 

2-1. Encode the query to an embedding

- 사용자가 입력한 질문(Question) 텍스트를 Jina의 Document 타입으로 정의한다.

- Query Flow에 Encoder 추가한다. Index Flow와 마찬가지로 동일한 Encoder를 사용하여 질문을 Encoding한다.

- Query Flow에서 pods/encode.yml의 동일한 Encoder를 사용할 수 있다. flows 폴더에 query.yml 파일을 만들고 여기에 Encoder Pod를 추가한다.

!Flow
with:
  read_only: true
pods:
  encoder:
    uses: pods/encode.yml
    parallel: $JINA_PARALLEL
    timeout_ready: 600000
    read_only: true

 

2-2. Find the most similar similar matches from the Index

- 질문을 인코딩한 후 질문 임베딩은 드라이버에 의해 문서에 추가된다.

- 그런 다음 이 문서는 다음 Pod의 인덱서로 전송되고 Driver는 질문 임베딩을 인덱서로 전달한다.

- 그런 다음 인덱서는 k-nearest neighbors algorithm을 사용하여 가장 유사한 임베딩이 포함된 답변을 검색하고 Document에 추가할 Driver에 Top-k answer 일치 목록을 전달한다. 

- 일치 항목에는 docid, doc 및 일치하는 match score와 같은 데이터가 포함된다.

- Index Flow에서 동일한 Indexer를 사용하고 있으므로 Indexer Pod를 flow/query.yml 에 추가하면 된다.

!Flow
with:
  read_only: true
pods:
  encoder:
    uses: pods/encode.yml
    parallel: $JINA_PARALLEL
    timeout_ready: 600000
    read_only: true
  doc_indexer:
    uses: pods/doc.yml
    shards: $JINA_SHARDS
    separated_workspace: true
    polling: all
    uses_reducing: _merge_all
    timeout_ready: 100000

 

2-3. Rerank the scores of the matches

- Indexer가 이 시점에서 Top-k개 answer 일치 항목을 반환하고 더 나은 결과를 얻기 위해 match score를 다시 계산한다고 가정해보자. Jina에는 Rankers라는 Executors class가 있는데, 특히 Match2DocRankers는 new score를 계산하여 쿼리의 match score를 다시 매긴다. Jina Hub의 Rankers를 보면, Levenshtein Ranker는 Levenshtein distance를 사용하여 match score를 다시 계산한다.

- 그러나 disteance matric을 사용하여 점수를 다시 계산하는 대신, Ranker에 fine-tuned(미세 조정된) BERT 모델인 FinBERT-QA를 로드하고 binary classification task에 대한 입력으로 질문과 현재 일치 답변의 연결을 사용하여 점수를 다시 계산한다.

- 여기서 주요 아이디어는 FinBERT-QA에서 계산한 relevancy score를 기준으로 재정렬된 일치 목록을 반환하기 위해 query text와 일치 항목(answer text 및 match score 포함)을 랭커에 전달하는 것이다. Driver는 다시 정렬된 목록을 기반으로 Document의 일치 항목을 업데이트한다.

** Build a Custom Executor("더보기"를 눌러보시오)**

더보기

1) Set up

- Executor type 중 4번 Ranker를 선택한다.

jina hub new

 

2) Fill in the logit for reranking

- __init__.py 파일에 아래와 같은 로직을 구현한다.

 from jina.executors.rankers import BaseRanker
            
 class FinBertQARanker(BaseRanker):
     """
     :class:`FinBertQARanker` recomputes match scores using FinBERT-QA.
     """
        
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
         # your customized __init__ below
         raise NotImplementedError
        
     def score(self, *args, **kwargs):
         raise NotImplementedError

 

- Jina는 다양한 Executor 클래스를 포함하고 있으며, 여기서 사용할 기본 Ranker 클래스는 Match2DocRankers라고 하며, 일치 점수를 다시 계산하는 기능이 있다.

- 먼저 BaseRanker의 기본 클래스를 Match2DocRanker로 변경한다. 또한 Jina와 현재 디렉토리를 정의할 뿐만 아니라 필요한 다른 모듈을 사용하여 PyTorch를 가져온다.

 import os
 from typing import Dict
 import numpy as np
    
 from jina.executors.devices import TorchDevice
 from jina.executors.rankers import Match2DocRanker
    
 cur_dir = os.path.dirname(os.path.abspath(__file__))

 

 

- 로직은 Jina의 TorchDevice 및 Match2DocRanker를 사용하는 FinBertQARanker 클래스에서 구현된다. 나중에 Dockerfile에 필요한 모델을 다운로드한다.

- models 폴더에 (1) bert-qa 및 (2) 2_finbert-qa-50_512_16_3e6.pt 라는 두 가지 모델이 있다고 가정해보자.

(1) bert-qa : BERT를 사용한 Passage Re-ranking의 MS 매크로 데이터 세트에서 미세 조정된 bert-base-uncased

(2) 2_finbert-qa-50_512_16_3e6.pt: FinBERT-QA 모델 - FiQA 데이터 세트에서 미세 조정된 bert-qa

- 먼저 초기화에 사용할 사전 훈련된 모델로 bert-qa/를 지정하고

- QA relevancy score를 계산하는 데 사용할 모델로 2_finbert-qa-50_512_16_3e6.pt를 지정하고

- QA의 최대 시퀀스 길이를 지정한다.

class FinBertQARanker(TorchDevice, Match2DocRanker):
     """
     :class:`FinBertQARanker` Compute QA relevancy scores using a fine-tuned BERT model.
     """
    
     required_keys = {"text"}
    
     def __init__(
             self,
             pretrained_model_name_or_path: str = os.path.join(cur_dir, "models/bert-qa"),
             model_path: str = os.path.join(cur_dir, "models/2_finbert-qa-50_512_16_3e6.pt"),
             max_length: int = 512,
             *args, **kwargs):
         """
         :param pretrained_model_name_or_path: the name of the pre-trained model.
         :param model_path: the path of the fine-tuned model.
         :param max_length: the max length to truncate the tokenized sequences to.
         """
    
         super().__init__(*args, **kwargs)
         self.pretrained_model_name_or_path = pretrained_model_name_or_path
         self.model_path = model_path
         self.max_length = max_length

 

- 그런 다음 Hugging Face transformer을 사용하여 binary classification task에 대한 모델을 로드하기 위해 클래스에 post_init 함수를 추가한다. evaluation mode에서 모델을 설정해야 한다.

     def post_init(self):
         super().post_init()
         import torch
         from transformers import BertForSequenceClassification, AutoTokenizer
    
         self.device = torch.device("cpu")
         self.tokenizer = AutoTokenizer.from_pretrained(self.pretrained_model_name_or_path, do_lower_case=True)
         self.model = BertForSequenceClassification.from_pretrained(self.pretrained_model_name_or_path, cache_dir=None, num_labels=2)
         self.model.load_state_dict(torch.load(self.model_path, map_location=self.device), strict=False)
         self.to_device(self.model)
         self.model.eval()

 

-  _get_score 함수를 구현하여 질문의 relevancy score와 Top-k개 매칭된 답변을 각각 계산한다. 먼저 질문과 각 top-k 답변을 연결하고 변환하여 tokenizer를 사용하여 모델에 필요한 입력( input_ids, token_type_ids, att_mask)을 얻기 위해 인코딩한다.

- 그런 다음 input을 모델에 입력하고 QA 쌍이 관련이 있다는 prediction score를 얻는다(label = 1). prediction score를 0과 1 사이의 확률로 변환하기 위해 점수에 softmax 함수를 적용한다. output은 QA 쌍에 대한 확률 형태의 relevancy score가 된다.

     def _get_score(self, query, answer):
         import torch
         from torch.nn.functional import softmax
    
         # Create input embeddings for the model
         encoded_seq = self.tokenizer.encode_plus(query, answer,
                                             max_length=self.max_length,
                                             pad_to_max_length=True,
                                             return_token_type_ids=True,
                                             return_attention_mask=True)
         # Numericalized, padded, clipped seq with special tokens
         input_ids = torch.tensor([encoded_seq['input_ids']]).to(self.device)
         # Specify which position of the embedding is the question or answer
         token_type_ids = torch.tensor([encoded_seq['token_type_ids']]).to(self.device)
         # Specify which position of the embedding is padded
         att_mask = torch.tensor([encoded_seq['attention_mask']]).to(self.device)
         # Don't calculate gradients
         with torch.no_grad():
             # Forward pass, calculate logit predictions for each QA pair
             outputs = self.model(input_ids, token_type_ids=token_type_ids, attention_mask=att_mask)
         # Get the predictions
         logits = outputs[0]
         # Apply activation function to get the relevancy score
         rel_score = softmax(logits, dim=1)
         rel_score = rel_score.numpy()
         # Probability that the QA pair is relevant
         rel_score = rel_score[:, 1][0]
    
         return rel_score

 

- 마지막으로, 사용자와 Jina의 match score를 입력으로 가져와서 _get_scores를 사용하여 new score를 다시 계산하는 scoring function 를 만든다.

     def score(
             self, query_meta: Dict, old_match_scores: Dict, match_meta: Dict
     ) -> "np.ndarray":
    
         new_scores = [
             (
                 match_id,
                 self._get_score(query_meta['text'], match_meta[match_id]['text']),
              )
             for match_id, old_score in old_match_scores.items()
         ]
         return np.array(
             new_scores
             ,
             dtype=[(self.COL_MATCH_ID, np.int64), (self.COL_SCORE, np.float64)],
         )

 

3) Write a Unit Test

- 새로운 Executor를 생성하고 Jina Hub API를 사용하여 Docker 이미지를 빌드하려면 단위 테스트를 작성해야 한다. tests/test_finbertqaranker.py에서 이에 대한 템플릿을 찾을 수 있다. 쿼리가 주어진 두 개의 답변 일치에 대한 관련성 확률을 계산하고 FinBertQARanker가 우리의 예상과 동일한 점수를 계산하는지 확인하기 위해 간단한 검사를 작성한다.

 import copy
 import json
 import numpy as np
 import torch
 from torch.nn.functional import softmax
 from transformers import BertTokenizer, BertForSequenceClassification
    
 from jina.executors.rankers import Match2DocRanker
    
 from .. import FinBertQARanker
    
 def test_finbertqaranker():
     """here is my test code
    
     https://docs.pytest.org/en/stable/getting-started.html#create-your-first-test
     """
     query_meta = {"text": "Why are big companies like Apple or Google not included in the Dow Jones Industrial "
                           "Average (DJIA) index?"}
     query_meta_json = json.dumps(query_meta, sort_keys=True)
     old_match_scores = {1: 5, 2: 7}
     old_match_scores_json = json.dumps(old_match_scores, sort_keys=True)
     match_meta = {1: {"text": "That is a pretty exclusive club and for the most part they are not interested in "
                               "highly volatile companies like Apple and Google. Sure, IBM is part of the DJIA, "
                               "but that is about as stalwart as you can get these days. The typical profile for a "
                               "DJIA stock would be one that pays fairly predictable dividends, has been around since "
                               "money was invented, and are not going anywhere unless the apocalypse really happens "
                               "this year. In summary, DJIA is the boring reliable company index."},
                   2: {"text": "In  most  cases  you  cannot  do  reverse  lookup  on  tax  id  in  the  US.  You  can "
                               " verify ,  but  for  that  you  need  to  have  more  than  just  the  FEIN/SSN.  You  "
                               "should  also  have  a  name ,  and  some  times  address.  Non-profits ,  specifically "
                               ",  have  to  publish  their  EIN  to  donors ,  so  it  may  be  easier  than  others  "
                               "to  identify  those.  Other  businesses  may  not  be  as  easy  to  find  just  by  "
                               "EIN."}}
     match_meta_json = json.dumps(match_meta, sort_keys=True)
    
     pretrained_model = 'models/bert-qa'
     model_path = "models/2_finbert-qa-50_512_16_3e6.pt"
    
     ranker = FinBertQARanker(pretrained_model_name_or_path=pretrained_model, model_path=model_path)
    
     new_scores = ranker.score(
         copy.deepcopy(query_meta),
         copy.deepcopy(old_match_scores),
         copy.deepcopy(match_meta)
     )
    
     # new_scores = [(1, 0.7607551217079163), (2, 0.0001482228108216077)]
     np.testing.assert_approx_equal(new_scores[0][1], 0.7607, significant=4)
     np.testing.assert_approx_equal(round(new_scores[1][1], 4), 0.0001, significant=4)
    
     # Guarantee no side-effects happen
     assert query_meta_json == json.dumps(query_meta, sort_keys=True)
     assert old_match_scores_json == json.dumps(old_match_scores, sort_keys=True)
     assert match_meta_json == json.dumps(match_meta, sort_keys=True)

 

4) Add Requirements

- Jina 외에도 FinBertQARanker용 PyTorch 및 transformer를 사용하고 있으므로 FinBertQARanker/requirements.txt에 추가한다.

torch==1.7.1
transformers==4.0.1

 

** Dockerfile 관련해서는 아래 내용 참고!

5) Prepare Dockerfile

- Dockerfile을 아래 내용으로 변경하면 모델이 models/이라는 폴더에 다운로드된다.

 

6) Build Docker image with Jina Hub API

- FinBertQARanker를 Docker 이미지로 빌드할 준비가 완료된 후, 작업 디렉토리에 아래 내용을 입력한다.

--pull : 로컬이 아닌 경우 Jina 기본 이미지를 다운로드한다.

--test-uses : 빌드된 이미지가 Jina의 Flow API를 통해 성공적으로 테스트 실행될 수 있는지 확인하는 추가 테스트를 추가한다.

--timeout-ready : post_init 함수에 모델을 로드할 시간을 준다.

5) Prepare Dockerfile

- Dockerfile을 아래 내용으로 변경하면 모델이 models/이라는 폴더에 다운로드된다.

FROM jinaai/jina
    
 RUN apt-get update && \
     apt-get -y install wget && \
     apt-get -y install unzip
    
 COPY ./requirements.txt /
 RUN pip install -r requirements.txt
 RUN pip uninstall -y dataclasses
 RUN pip install pytest
    
 RUN wget https://www.dropbox.com/s/sh2h9o5yd7v4ku6/bert-qa.zip
 RUN wget https://www.dropbox.com/s/12uiuumz4vbqvhk/2_finbert-qa-50_512_16_3e6.pt
    
 # setup the workspace
 COPY  . /workspace
 WORKDIR /workspace
    
 RUN if [ ! -d models ]; then \
     mkdir models/ && cd models/ && \
     mv /bert-qa.zip                   . && \
     mv /2_finbert-qa-50_512_16_3e6.pt . && \
     mkdir bert-qa && \
     unzip bert-qa.zip -d bert-qa/; \
 fi
    
 RUN pytest
    
 ENTRYPOINT ["jina", "pod", "--uses", "config.yml"]

 

6) Build Docker image with Jina Hub API

- FinBertQARanker를 Docker 이미지로 빌드할 준비가 완료된 후, 작업 디렉토리에 아래 내용을 입력한다.

jina hub build FinBertQARanker/ --pull --test-uses --timeout-ready 60000

--pull : 로컬이 아닌 경우 Jina 기본 이미지를 다운로드한다.

--test-uses : 빌드된 이미지가 Jina의 Flow API를 통해 성공적으로 테스트 실행될 수 있는지 확인하는 추가 테스트를 추가한다.

--timeout-ready : post_init 함수에 모델을 로드할 시간을 준다.

 

1) Create a custom Ranker Pod

- 사용자 지정 Ranker인 FinBertQARanker를 사용하려면 먼저 Ranker에 대한 새 Pod를 만든다. pods 폴더에 rank.yml 파일을 생성한 후 FinBertQARanker/config.yml의 내용을 pods/rank.yml로 복사한다.

- Executor는  FinBertQARanker/__init__.py에서 구현한 로직을 사용하도록 Query Flow에 지시한다. 이 구현을 위한 코드는 Docker 이미지의 workspace 폴더 안에 로드되므로 __init__.py 앞에 workspace/를 추가한다.

- 지금까지 사용한 Encoder 및 Indexer Executor는 모두 Pod의 기본 Driver를 사용한다. Custom Executor를 생성했으므로 Ranker Pod에 사용할 Driver를 알려야 합니다. 이 경우 Match2DocRanker 기본 Ranker 클래스에 Matches2DocRankDriver를 사용한다.

!FinBertQARanker
with:
    {}
metas:
    py_modules:
    - workspace/__init__.py
    # - You can put more dependencies here
requests:
    on:
    [ SearchRequest ]:
        - !Matches2DocRankDriver { }

 

2) Use Custom Ranker in the Query Flow

- 다른 Executor Pod와 마찬가지로 doc_indexer 뒤에 ranker를 추가하고 태그 이름 앞에 docker:// 접두사를 지정하여 방금 생성한 Docker 이미지와 Ranker Pod를 사용하도록 Query Flow에 지시한다.

!Flow
with:
  read_only: true
pods:
  encoder:
    uses: pods/encode.yml
    parallel: $JINA_PARALLEL
    timeout_ready: 600000
    read_only: true
  doc_indexer:
    uses: pods/doc.yml
    shards: $JINA_SHARDS
    separated_workspace: true
    polling: all
    uses_reducing: _merge_all
    timeout_ready: 100000
  ranker:
    uses: docker://jinahub/pod.ranker.finbertqaranker:0.0.1-0.8.23
    uses_internal: pods/rank.yml
    parallel: $JINA_PARALLEL
    timeout_ready: 600000

 

- Docker 이미지의 태그 이름은 현재 Jina 릴리스에 따라 변경될 수 있기 때문에 그때마다 태그 이름을 변경해야 한다.

- Flow API를 사용하여 쿼리 흐름을 다시 시각화할 수 있다.

from jina.flow import Flow

f = Flow.load_config('flows/query.yml')
f.plot()

 

- 여기에서 Encoder인 encoder 및 Indexer인 doc_indexer 및 Ranker인 ranker를 포함하는 세 개의 Pod가 있는 Query Flow을 볼 수 있다.

- Query Flow이 끝나면 Ranker Pod의 Driver가 Custom Ranker인 FinBertQARanker가 계산한 확률을 기반으로 Document의 일치 항목을 재정렬된 일치 목록으로 변경한다.

 

2-4. Get matches and scores

* Build a Search Application

- 최종 일치 항목과 relevancy 확률이 Document에 저장되어 있기 때문에 app.py에서 사용자 입력의 질문에 대한 응답을 출력하는 함수를 작성한다.

- Document, d.matches에서 일치 항목을 반복하고 점수 값과 일치하는 답변 텍스트를 출력한다.

def print_resp(resp, question):
    for d in resp.search.docs:
        print(f"🔮 Ranked list of answers to the question: {question}: \n")

        for idx, match in enumerate(d.matches):
            score = match.score.value
            answer = match.text.strip()
            print(f'> {idx+1:>2d}. "{answer}"\n Score: ({score:.2f})')

 

- 그런 다음 flows/query.yml의 Query Flow을 사용하는 검색 방법을 작성하고 사용자 입력을 print_resp에 전달한다.

- f.search_lines()에서 input을 사용자 쿼리로 지정하고 output을 출력할 응답으로 지정하며 검색하려는 top-k개 답변을 지정한다.

- f.search_lines()의 멋진 점은 사용자 쿼리에 대한 문서를 자동으로 생성한다는 것이다.

def search():
    f = Flow.load_config('flows/query.yml')

    with f:
        while True:
            text = input("Please type a question: ")
            if not text:
                break

            def ppr(x):
                print_resp(x, text)

            f.search_lines(lines=[text, ], output_fn=ppr, top_k=50)

 

 

- 공부하는데 참조한 곳

1) https://towardsdatascience.com/how-to-build-a-production-ready-financial-question-answering-system-with-jina-and-bert-48335103043f

 

 

내용에 문제가 발견되면 알려주십시오!

신속하게 수정하겠습니다.

 

그럼 이만.

 

 

'Study' 카테고리의 다른 글

multiprocessing 겅부..  (0) 2022.09.18
local에서 ssh 서버에 띄운 jupyter notebook 붙기  (0) 2022.02.06
BERT 이해하기  (0) 2021.09.15
머신러닝 개념 정리  (0) 2021.05.22

"나의 첫 머신러닝/딥러닝" 책으로 공부하며 정리하는 시간!

1. 지도학습과 비지도학습

지도학습(Supervised learning)이란?

  - 정답을 알려주면서 진행되는 학습

  - 학습시 데이터와 함께 레이블(정답)이 항상 제공되어야 함

  - 정답 ≒ 실제값 ≒ 레이블 ≒ 타깃 ≒ 클래스 ≒ y값

  - 주로 주어진 데이터와 레이블을 이용해 새로운 데이터의 레이블을 예측해야 할 때 사용됨

  - 머신러닝 모델을 통해 예측된 값 = 예측값 ≒ 분류값 ≒ y hat

  - 장점 : 테스트할 때 데이터와 함께 레이블을 제동해서 손쉽게 모델의 성능을 평가할 수 있음

  - 단점 : 테이터마다 레이블을 달기 위해 많은 시간을 투자해야 함

  - 대표적인 예 : 분류, 회기

 

비지도학습(Unsupervised learning)이란?

  - 레이블(정답)이 없이 진행되는 학습

  - 학습할 때 레이블 없이 데이터만 필요

  - 데이터 자체에서 패턴을 찾아내야 할 떄 사용

  - 장점 : 레이블을 제공할 필요가 없음

  - 단점 : 레이블이 없기 때문에 모델 성능을 평가하는 데에 다소 어려움이 있음

  - 대표적인 예 : 군집화, 차원축소

 

2. 분류와 회귀

분류란?

  - 데이터가 입력됐을 때 지도학습을 통해 미리 학습된 레이블 중 하나 또는 여러개의 레이블로 예측

  - 분리된 값으로 예측

      1) 이진분류

      - (예, 아니오), (남자, 여자)와 같이 둘 중 하나의 값으로 분류하는 경우

      2) 다중분류

      - (빨강, 녹색, 파랑) 중 하나의 색으로 분류하거나,

        0부터 9까지의 손글씨 숫자 중 하나의 숫자로 분류하기처럼 여러 개의 분류값 중에서 하나의 값으로 예측

      3) 다중 레이블 분류

      - 데이터가 입력됐을 때 두 개 이상의 레이블로 분류

 

회귀란?

  - 입력된 데이터에 대해 연속된 값으로 예측

  - 날씨를 더움, 보통, 추움이라는 3가지로만 예측하는 분류와 달리,

    회귀는 35도, 34.5도, 34도와 같이 정해진 레이블이 아닌 연속성을 가진 수치로 예측

 

[출처]

허민석, 『나의 첫 머신러닝/딥러닝』, 위키북스(2019), p14-15.

'Study' 카테고리의 다른 글

multiprocessing 겅부..  (0) 2022.09.18
local에서 ssh 서버에 띄운 jupyter notebook 붙기  (0) 2022.02.06
BERT 이해하기  (0) 2021.09.15
Neural Search 관련 공부  (2) 2021.07.07

우분투 서버에 열심히 Java, MySQL, Tomcat, Apache를 설치하고 연동까지 다 해서,

BOMBEE 라는 war 파일을 만들어서 배포를 하려고 시도했는데 계속 톰캣 아래의 webapps/ROOT/ 에서 index.html 파일을 불러오는 것이다.


이게 아닌데, BOMBEE 아래에 있는 index.html 을 불러와야 하는데....

설정 파일에도 저렇게 경로를 잘 적어주었지만, 제대로 불러오지 못하는 문제가 생겼다!

그래서 무엇이 문제인가 한참 고민을 하다, 같이 스터디를 진행했던 브라더에게 물어보니,

server.xml 파일을 변경하란다!!!


이것이 정답이였다.

Tomcat의 context root 설정을 아래와 같이 변경해주고 나서야 제대로 화면이 뜨는 것을 확인할 수 있었다!!!!

절대 까먹지 말자!!


아! 그리고 혹시나 또 까먹고 들어올 나를 위해,

war파일로 export 시키기 전에 이클립스에서 나의 프로젝트를 오른쪽 클릭한 후,

Properties를 눌러라!

그리고 나서 Web Project Settings에서 Context root 를 / 로 설정해주고

war파일로 만들어라 멍청아!!!!!!!!!!!!!!!!!!!!!!


오늘은 아파치와 톰캣을 연동할 것이다.


Why 아파치와 톰캣을 연동하니?

톰캣은 WAS이지만 Web 서버의 기능도 갖고 있어서 사용하는데 문제가 생기지는 않지만,

톰캣의 Web 서버 기능은 아파치보다 처리 속도가 느리기 때문에 정적인 페이지는 아파치가 처리하고,

동적인 페이지는 톰캣이 처리할 수 있도록 해주기 위해 아파치와 톰캣을 연동한다.

이렇게 연동시키게 되면 부하를 분산시킬 수 있다.


아파치(Apache)를 설치해보자!!


1. 설치 전 패키지 업데이트를 먼저 진행한다.

# 패키지 업데이트 명령어

apt-get update


2. 업데이트가 끝나면 아파치를 설치한다.

# 아파치 설치 명령어

apt-get install apache2


설치 중간에 계속 할거니? 라고 물어보면 Y를 입력하여 설치를 계속 진행한다.


3. 설치가 끝났으면 확인을 해보자.

# 아파치 버전 확인 명령어

apache2 -v


netstat은 시스템의 네트워크 연결 목록(tcp, udp, 소켓 연결)을 보여주는 유틸리티이다.

netstat을 사용하여 아파치가 정상적으로 동작하고 있는지 화인한다.


# 아파치 네트워크 연결 목록 확인 명령어

netstat -ntlp | grep apache2


4. 다음으로 아파치 포트로 접속할 수 있도록 방화벽에 등록한다.(80은 아파치의 기본 포트이다.)

# 방화벽에 아파치 포트 허용하도록 등록하는 명령어

ufw allow 80/tcp


5. 방화벽에 등록까지 끝났으면 이제 아파치를 실행해보자.

# 아파치 실행 명령어

/etc/init.d/apache2 start

또는

service apache2 start


6. 아파치가 실행되면 확인을 위해 서버 주소를 입력한다.



위와 같은 화면이 나오면 정상적으로 설치 된 것이다.


7. 아파치(Apache2)와 톰캣(Tomcat8)을 연동하기 위해 연동커넥터인 mod_jk를 설치한다.

# mod_jk 설치 명령어

apt-get install libapache2-mod-jk


8. /etc/apache2/ 경로에 properties 파일을 생성한다.

#workers.properties 파일 생성 명령어

nano /etc/apache2/workers.properties


9. 파일을 생성했으면 그 파일에 아래의 내용을 입력한다.

#properties 파일에 입력할 내용

workers.tomcat_home=톰캣 설치 경로

workers.java_home=jdk 설치경로


# Define 1 real worker ajp13

worker.list=임의의 이름


# Set properties for tomcat1 (ajp13)

# 포트는 tomcat server.xml 파일 AJP/1.3 Connector의 Port

worker.임의의 이름.port = 포트

worker.임의의 이름.host = 톰캣서버 아이피 주소

# apache + tomcat 통신 프로토콜

worker.임의의 이름.type = ajp13

worker.임의의 이름.lbfactor = 1


workers.tomcat_home=/usr/share/tomcat8

workers.java_home=/usr/lib/jvm/java-8-openjdk-amd64


# Define 1 real worker ajp13

worker.list=tomcat1


# Set properties for tomcat1 (ajp13)

# 포트는 tomcat server.xml 파일 AJP/1.3 Connector의 Port

worker.tomcat1.port = 8009

worker.tomcat1.host = 13.125.126.162

# apache + tomcat 통신 프로토콜

worker.tomcat1.type = ajp13

worker.tomcat1.lbfactor = 20



내용을 다 입력했으면 Ctrl + X → Y → 엔터 를 입력하여 저장한다.


10. 다음으로 jk.conf 파일을 수정한다.

#jk.conf 파일 여는 명령어

nano /etc/apache2/mods-available/jk.conf


아래 사진의 빨간색으로 표시된 부분을 찾아 주석으로 막아준다.


그 후 아래 사진처럼 내용을 입력한다. 이렇게 하는 이유는 /etc/apache2/ 경로에 생성한 workers.properties로부터 설정 정보를 가져오기 위해서이다.


내용을 입력한 후 Ctrl + X → Y → 엔터 를 입력하여 저장한다.


11. 이제 000-default.conf 파일을 수정해보자.

#000-default.conf 파일 여는 명령어

nano /etc/apache2/sites-available/000-default.conf


빨간색으로 표시된 DocumentRoot /var/www를 주석처리한다. 그리고 아래에 톰캣의 webapps 아래 실행할 웹 프로젝트 경로를 적어준다.


webapps아래에 BOMBEE 라는 폴더에 프로젝트를 배포하기 때문에 DocumentRoot /var/lib/tomcat8/webapp/BOMBEE라고 입력해줬다.

(ROOT폴더에 웹 프로젝트를 배포하려면 DocumentRoot /var/lib/tomcat8/webapp/ROOT/ 라고 입력하면 된다.)


JkMount /* tomcat1 부분은 모든 URL(/*)을 톰캣에게 바인딩하는 부분이다. tomcat1은 workers.properties에 입력한 연동할 톰캣의 이름이다.


만약 톰캣에 배포된 프로젝트가 여러 개이고 특정 프로젝트만 URL을 바인딩 해야 한다면, /* 부분을 수정해주면 된다.

예를 들어 배포된 프로젝트가 pro1, pro2 이렇게 있는데 pro1만 바인딩하려면 JkMount /pro1/* tomcat1 이렇게 수정하면 된다.



12. 다음으로 수정해야 할 파일은 톰캣의 server.xml 파일이다.

#server.xml 파일 여는 명령어

nano /etc/tomcat8/server.xml


빨간색으로 표시된 부분의 주석을 없애준다.

13. 아파치와 톰캣을 재시작한다.

#아파치와 톰캣 재시작 하는 명령어

service apache2 restart

service tomcat8 restart


14. 브라우저에서 확인해본다.

서버에 배포된 웹 프로젝트를 실행하기 위해 서버IP주소:8080을 입력한다.


주소창에서 8080을 지우고 접속해도 웹 프로젝트가 실행되는 것을 확인할 수 잇다.


여기까지 되면 정상적으로 연동된것이다.

끝!




[출처]

http://sisiblog.tistory.com/12

http://all-record.tistory.com/188

앞서 톰캣을 설치했으니, MySQL까지 설치해보자.


1. 먼저 설치가능한 MySQL 전을 확인한다. (여기서 나는 5.7버전을 설치할 것이다.)

# MySQL 버전 확인 명령어

apt-cache search mysql-server



2. 버전을 확인한 후, MySQL 설치 명령어를 실행한다.

# MySQL 설치 명령어

apt-get install mysql-server-5.7 -y



설치 도중에 MySQL root(최상위 계정)의 비밀번호를 입력하는 부분이 나온다.

잊어버리지 않을, 절대 기억할 수 있는 비밀번호를 입력하고 엔터를 누른다.



앞에 입력했던 비밀번호를 확인하는 창이 나온다. 좀 전에 입력한 root 비밀번호를 다시 한 번 입력한다.



3. 설치가 끝났으면 MySQL이 정상적으로 설치되었는지 확인한다.

# MySQL 설치 확인 명령어

/etc/init.d/mysql status

netstat -ntlp | grep mysqld




이렇게 뜨면 정상적으로 잘 설치된 것이다!


4. MySQL에 접속하여 Database를 조회한다. (제대로 설치됬는지 다시 한 번 확인하기 위해서!!!!!!!!!)

# MySQL 접속 명령어

mysql -u root -p -e'show databases'

입력 후에 비밀번호를 입력한다.



이렇게 나온다면 정상적으로 설치된 것이다!


5. 다음으로 MySQL 한글 설정을 하기 위해 설정이 있는 곳으로 이동한다.

MySQL은 기본 언어셋이 latin1로 되어있다. latin1로 되어있을 경우 한글 입력 시 깨지는 문제가 발생한다.

이 문제를 해결하기 위해 설치 후에 반드시 한글 설정을 따로 해줘야 한다.

# MySQL 설정 파일이 있는 곳으로 이동하는 명령어

cd /etc/mysql/conf.d


# 파일 목록 보는 명령어

ls


설정 파일이 들어있는 곳으로 이동한 후에 ls 명령어를 통해 폴더 안에 어떤 파일들이 있는지 확인해본다.



여기서 우리가 한글 설정할 때 사용할 파일은 mysql.cnf 이다.


6. 명령어를 입력하여 mysql.cnf 파일을 열어 수정할 수 있도록 한다.

# MySQL 설정 파일 열기 위한 명령어

nano mysql.conf



7. 파일을 열었을 때 입력되어 있는 [mysql]을 지운 후, 설정 내용을 입력한다.

# MySQL 설정 파일에 입력할 내용

[mysqld]

datadir=/var/lib/mysql

socket=/var/lib/mysql/mysql.sock

user=mysql

character-set-server=utf8

collation-server=utf8_general_ci

init_connect = set collation_connection = utf8_general_ci

init_connect = set names utf8

 

[mysql]

default-character-set=utf8

 

[mysqld_safe]

log-error=/var/log/mysqld.log

pid-file=/var/run/mysqld/mysqld.pid

default-character-set=utf8

 

[client]

default-character-set=utf8

 

[mysqldump]

default-character-set=utf8




설정 내용을 다 입력한 후, Ctrl + X → Y → 엔터 를 눌러 저장한다.


8. 설정이 끝났으면 MySQL재시작한다.

# MySQL 재시작을 위한 명령어

/etc/init.d/mysql restart



9. 설정이 잘 반영 되었는지 확인 해보자. MySQL root 계정으로 로그인하여 status 명령어를 통해 확인한다.

# MySQL root 계정으로 로그인 하는 명령어

mysql -u root -p

입력 후에 비밀번호를 입력한다.



# MySQL 상태 보는 명령어 명령어

status


표시된 부분이 utf8로 변경되었다면 정상적으로 설정이 반영된 것이다.

여기까지 MySQL 설치 및 설정 하는 방법 끝!


MySQL 사용자 계정 생성 및 외부 접속 설정은 다음 포스팅에서!!!!




[참고]

http://all-record.tistory.com/183?category=733055


오늘은 우분투 서버에 톰캣(Tomcat)을 설치하고 실행까지 해보자.

웹 서버 구축을 위해서는 WAS(Web Application Server)인 톰캣이 필요하다.


여기서 잠깐!

WAS(Web Application Server)란?

인터넷상에서 HTTP를 통해 사용자 컴퓨터나 장치에 애플리케이션을 수행해주는 미들웨어(소프트웨어 엔진)이라고 우리 모두의 백과사전 위키백과가 알려준다.

웹 애플리케이션 서버는 동적 서버 콘텐츠를 수행하는 것으로 일반적인 웹 서버와 구별이 되며, 주로 데이터베이스 서버와 같이 수행이 된다.


톰캣을 사용하는 이유?

톰캣은 개발자가 만든 애플리케이션을 실행할 수 있는 환경을 제공하는 서블릿 컨테이너 역할을 한다.

따라서 웹 애플리케이션 파일을 톰캣 파일 아래에 저장해둔다.


자, 이제 설치하러 가보자.


1. 일단, 톰캣을 설치한다.

# 톰캣 설치를 위한 명령어

apt-get install tomcat8 -y

(나는 톰캣 8버전을 사용할 것이다.)



2. 설치를 다 했으면 톰캣의 버전을 확인한다.

# 톰캣 버전 확인을 위한 명령어

/usr/share/tomcat8/bin/version.sh



3. 버전까지 확인이 끝난 후, 톰캣이 정상적으로 동작하는지 테스트한다.

여기서 잠깐!!!!

톰캣의 포트를 외부에서 접속이 되도록 방화벽에서 변경해야 한다. 

톰캣의 기본 포트는 8080이다. (만약 포트를 변경했다면 변경한 포트를 적어주면 된다.)

# 톰캣 포트를 외부에서 접속 가능하도록 방화벽에 등록해주는 명령어

ufw allow 8080(톰캣포트번호)/tcp



4. 이제 톰캣을 실행시켜보자!

# 톰캣 실행시키는 명령어

service tomcat8 start



5. 톰캣을 실행시켰으면, 제대로 작동하는지 확인을 해야한다. 그러기 위해서 서버 아이피주소:포트를 입력해보자.


이렇게 뜨면 정상적으로 잘 작동된 것이다!

톰캣 설치하고 작동시키기 끝!




[참고]

http://all-record.tistory.com/182?category=733055


오늘은 EC2를 이용해 만든 인스턴스인 우분투 서버에 JAVA를 설치할 것이다!


일단, SSH나 Telnet을 통해 작업할 때 원하는 간편하게 업로드 하거나 다운로드 받고 싶을 때 유용하게 사용할 lrzsz를 설치한다.

# 간편 업로드 및 다운로드를 위한 lrzsz 설치하기 명령어

apt-get install lrzsz


자, 이제 본격적으로 JAVA를 설치해보자!

1. JDK와 JRE 설치 명령어를 실행한다. 

(명령어마다 앞에 sudo를 붙여 관리자권한으로 실행시키기 귀찮으니 su - or su root 를 통해 관리자 권한으로 로그인했다.)

# JRE, JDK 설치하기 위한 명령어

apt-get install openjdk-8-jre

apt-get install openjdk-8-jdk



JDK 설치시 "계속 진행할거니?" 라고 물어보는데 이때 Y (응!)을 입력해주면 된다.


중간에 Y라고 입력하지 않고 바로 진행하려면 명령어 뒤에 -y 옵션을 달아준다.


2. 자바가 잘 설치되었는지 확인해보자.

# Java가 잘 설치되었나 확인하기 위한 버전 체크 명령어

java -version

javac -version


위에 있는 명령어를 실행하면 현재 설치된 자바 버전을 알 수 있다.


3. 이제 항상 헤매던 자바 환경변수 설정을 위해 자바가 설치된 위치를 확인해보자.

# Java 위치를 확인하기 위한 명령어

which javac

readlink -f /usr/bin/javac 

(위의 명령어에서 나온 경로를 -f 뒤에 적어준다.)


먼저 javac 위치를 확인한다.

여기서 잠깐!!

javac란?

javac는 오라클의 자바 개발 키트에 포함된 주요 자바 컴파일러이다. 

이 컴파일러는 자바 언어 사양을 충족하는 소스 코드를 받아들인 다음 자바 가상 머신 사양을 충족하는 바이트코드를 생성한다.

readlink ( get absolute path )란?

실제로는 symbolic link를 따라가서 마지막 값을 알려주는 명령어이지만, 일반 파일, 디렉토리에 대해서는 실제 절대 경로를 알려준다.

실행 결과를 보자.



javac의 실제 위치는 /usr/lib/jvm/java-8-openjdk-amd64/bin/javac 라는 것을 확인할 수 있다.

따라서, $JAVA_HOME은 /usr/lib/jvm/java-8-openjdk-amd64 으로 설정해야 한다.


4. 위치를 확인했으니 이제 환경변수 설정을 해보자. 첫 번째로 profile을 연다.

# profile을 열기 위한 명령어

nano /etc/profile



5. 파일을 열었으면 환경변수를 입력한다.

# profile에 입력해야 하는 환경변수

export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64

export PATH=$JAVA_HOME/bin/:$PATH

export CLASS_PATH=$JAVA_HOME/lib/:$CLASS_PATH


다 입력한 후에는 Ctrl + X → Y → 엔터를 눌러 저장한다.


6. 저장한 후에는 profile reload시킨다.

# profile을 reload하기 위한 명령어

source /etc/profile



7. 앞의 과정이 모두 끝났으면 우분투 서버를 재시작 한다.

# 우분투 서버 재시작하는 명령어

reboot now



8. 재시작 후 부팅이 끝났으면, echo 명령어를 이용하여 환경변수를 확인한다.

# 환경변수 확인하는 명령어

echo $JAVA_HOME

$JAVA_HOME/bin/javac -version



이렇게 잘 나오면 환경변수 설정 끝!





[출처]

https://ko.wikipedia.org/wiki/Javac

http://yolongyi.tistory.com/25

http://all-record.tistory.com/181


+ Recent posts