프로젝트/AWS winter camp - ELK&AWS 프로젝트

[AWS Cloud winter camp] 1/2 - 4일차

dayeonsheep 2024. 1. 2. 17:10

NLP Sneak peek

 

NLP Task -

기계는 사람처럼 언어를 학습하지 않아서 단계를 나누어 줘야 함

- low-level parsing : 문장분리/어근추출

- word and phrase level : 고유명사인식/형태소 분석/명사구 단위 분리/의존 구조 분석/참조 관계 분석

- sentence level : 감정 분석/기계 번역

- multi-sentence and paragraph level : 모순 관계 예측/질의 응답/대화형 챗봇/전문 요약

 

AI 관련 아이디어는 너무 길면 그 아이디어 빨리 쇠퇴함... 1-2달만 잡고 빨리 빨리 ... 하는 게 좋다!

 

word Embedding - BoW(bag of words) 

벡터화 - 기계가 실제로 이해할 수 있도록 - one-hot vector -단어별 고유 벡터 지정 ->문장을 벡터의 합으로 표현 가능

-> 제약점 : 차원이 무한정으로 필요함 => 비용이 많이 든다

 

word2Vec - 단어의 유사도를 거리로 표현 - 특정 단어를 벡터화 하면 그 상의 거리가 가까울수록 유사, 방향이 비슷하다면 관계도 비슷하다고 봄 (벡터 - 방향과 길이가 일치하면 같은 거니까 )

 

PCA - 가상 선 중에 분산이 가장 높은 걸 찾는... (주 성분 분석) 사람이 보기 편하도록

처음 input에 따라 output 무조건 같지 않음. 항상 다름 (apple인풋 / apple 아웃풋...)

 

벡터화 시킴으로 인해서 컴퓨터가 이해할 수 있도록 

이미지도 가능

문맥과의 관계... artistic한 거 이해 잘 못 함 

 

제일 연관없는 단어도 찾을 수 있음. 같은 분류가 아닌 단어 식별

 

nearest neighbors 

 

-

ES NLP

 

전통적인 검색 방법: 텍스트 매칭 - 실제 포함되어야지 찾을 수 있는

NLP 도입한 검색 방법 - 단어와 문장을 벡터로 인코딩해서 텍스트 임베딩

 

Brute force

-장점: 상위 결과를 다시 랭킹하거나 필터링함에 용이, 시간 공간이 있다면 무조건 답을 찾을 수 있음

-단점: 필드 레이어가 늘어나면... 비용 증가, 제한적임

 

greedy

HNSW & KNN

KNN: 최근접 이웃, 가장 가까운 쿼리-이웃-결과 등

다중 레이어로 구성

레이어마다 조금씩 이동, 찾으려는 파란색까지 도달하지 못할수도 있음

대용량 처리를 위해 약간의 정확도를 희생함

 

brute force 2번 하는 게 최악의 경우로 제한

 

brute force - 다 도는 거니까 다 돌면 너무 비효율적이니까 필터링 된 것만 돌음

HN은 전부 다 봄

6개... 1-4-3-5-7-6 이웃된... 통과된 노드

BF 이웃을 선택... 서치하고 바로 3-4 보고 끝 

사용자가 원하는건 최근접한 4번 분홍색을 찾는 게 목적이었기 때문에 걔 보고 끝임

 

최근접 이웃 찾기

-KNN (k - 대부분의 숫자 들어가는... 우리가 정하는 후보)

 

 

 

-

#벡터 임베딩을 위한 파이프라인 생성 
PUT _ingest/pipeline/vector_embedding_demo
{
  "processors": [
    {
      "inference": {
        "field_map": {
          "document": "text_field"
        },
        "model_id": "sentence-transformers__all-distilroberta-v1",
        "target_field": "ml.inference.document_vector",
        "on_failure": [
          {
            "append": {
              "field": "_source._ingest.inference_errors",
              "value": [
                {
                  "message": "Processor 'inference' in pipeline 'ml-inference-title-vector' failed with message '{{ _ingest.on_failure_message }}'",
                  "pipeline": "vector_embedding_demo",
                  "timestamp": "{{{ _ingest.timestamp }}}"
                }
              ]
            }
          }
        ]
      }
    },
    {
      "set": {
        "field": "document_vector",
        "if": "ctx?.ml?.inference != null && ctx.ml.inference['document_vector'] != null",
        "copy_from": "ml.inference.document_vector.predicted_value",
        "description": "Copy the predicted_value to 'document_vector'"
      }
    },
    {
      "remove": {
        "field": "ml.inference.document_vector",
        "ignore_missing": true
      }
    }
  ]
}

document: 이런 텍스트 필드를 쓸거야~ (한글 리뷰 텍스트)

model_id: 이런 모델을 쓸거야

document_vector에 도큐먼트 된 필드가 저장 됨

field: 에러 나면 inference_error가 날거야~

 

set : ml.inference.document_vector 이름 길어서 줄이는 작업

field : 사실 이름을 바꾼 게 아니고 복사한 거고 그거 지우기 (remove)

 

#벡터 임베딩을 위한 인덱스 템플릿 생성 
PUT _index_template/movie_ratings_vector
{
  "index_patterns": [
    "movie_ratings_vector"
  ],
  "priority": 1,
  "template": {
    "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 1,
      "index.default_pipeline": "vector_embedding_demo"
    },
    "mappings": {
      "properties": {
        "document_vector": {
          "type": "dense_vector",
          "dims": 768,
          "index": true,
          "similarity": "dot_product"
        },
        "document": {
          "type": "text"
        }
      },
      "_source": {
        "excludes": [
          "document_vector"
        ]
      }
    }
  }
}

인덱스 템플릿: 규칙

index_patterns: 이 인덱스 템플릿은 이렇게 들어갈거야~ 

index.default_pipeline : 디폴트 파이프라인(파이프라인의 목적은 벡터화)

 

mapping

document_vector : 어떤 타입인지 정확히 명시, 키워드나 텍스트-integer,double같은 벡터의 타입 dense_vector

dimension :768( , , , , ... 이 콤마가 768개 )

-> 이렇게 긴 벡터 읽기 힘듦 -> 인덱스 서치하면 보여줄 필요 없어 하는 설정이 excludes

 

document :text라고 명시해주는데, 안해주면 키워드랑 텍스트 둘 다 들어가서 낭비 됨

https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-deploy-models.html

 

Deploy trained models | Machine Learning in the Elastic Stack [8.11] | Elastic

 

www.elastic.co

 

인덱스 deploy 오류 나는 경우

failed 수 뜸

index 이름 바꿔서 다시 deploy 해주면 됨

 

그러면 다시 됨~

 

# KNN Query
GET movie_ratings_vector/_search
{
  "knn": [
    {
      "field": "document_vector",
      "k": 5,
      "num_candidates": 10,
      "query_vector_builder": {
        "text_embedding": {
          "model_id": "sentence-transformers__all-distilroberta-v1",
          "model_text": "갬덩"
        }
      }
    }
  ]
}

# KNN + BM25 Query
GET movie_ratings_vector/_search
{
  "size": 10,
  "query": {
    "match": {
      "document": "연기"
    }
  },
  "knn":{
      "field": "document_vector",
      "k": 5,
      "num_candidates": 10,
      "query_vector_builder": {
        "text_embedding": {
          "model_id": "sentence-transformers__all-distilroberta-v1",
          "model_text": "좋아"
        }
      }
    }
}

https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html

 

k-nearest neighbor (kNN) search | Elasticsearch Guide [8.11] | Elastic

Support for approximate kNN search was added in version 8.0. Before this, dense_vector fields did not support enabling index in the mapping. If you created an index prior to 8.0 containing dense_vector fields, then to support approximate kNN search the dat

www.elastic.co

"query": {
    "match": {
      "document": "연기" -> 연기가 있을 경우 스코어 올라감  
    }
  },

must not으로 특정 키워드 제외시킬 수 있음

 

 

 

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-boosting-query.html

 

Boosting query | Elasticsearch Guide [8.11] | Elastic

Returns documents matching a positive query while reducing the relevance score of documents that also match a negative query. You can use the boosting query to demote certain documents without excluding them from the search results. response = client.searc

www.elastic.co

특정 키워드나 조건 부스팅 걸어서 스코어 많이 올려주는 거

boost 1
boost 10

 

뉴스 기사는 타이틀이랑 내용... 

타이틀에 보통 중점 많이 두니까 knn을 비중을 두고 하는 게 잘 나옴

학술지 같은거는 내용에 좀 더 부스팅을 먹인다든지

 

 

이름 지정 안하고 올린 deployment는

한 번 더 올린 ID는 모델명_v1, v2이렇게 하면 안됨 모델인 게 아니기 때문에

명칭을 더 정확하게 dp1 dp2요렇게 해야 좋음

 

###삭제 쿼리 ! 주의 ! ###
POST _ml/trained_models/sentence-transformers__all-distilroberta-v1/deployment/_stop

포스트는 한 번 날리고 백단에서 하는 거임 

오래 걸리면 500에러 뱉음 -> 프론트 문제고 백에서는 계속 돌고 있어서 상관없음

실행 OK

v1 dp 삭제 된 거 확인 가능

dp stopped 된 것도 noti에서 확인 가능

 

 

-

#######################
# Text Processing I   #
#######################

"""
NLP에서 흔히하는 전처리는 소문자 변환, 앞뒤 필요없는 띄어쓰기를 제거하는 등의 텍스트 정규화 (text normalization)입니다.
"""

def normalize(input_string):
    """
     인풋으로 받는 스트링에서 정규화된 스트링을 반환함
     아래의 요건들을 충족시켜야함
    * 모든 단어들은 소문자로 되어야함
    * 띄어쓰기는 한칸으로 되어야함
    * 앞뒤 필요없는 띄어쓰기는 제거해야함
         Parameters:
             input_string (string): 영어로 된 대문자, 소문자, 띄어쓰기, 문장부호, 숫자로 이루어진 string
             ex - "This is an example.", "   EXTRA   SPACE   "
         Returns:
             normalized_string (string): 위 요건을 충족시킨 정규회된 string
             ex - 'this is an example.'
         Examples:
             >>> import text_processing as tp
             >>> input_string1 = "This is an example."
             >>> tp.normalize(input_string1)
             'this is an example.'
             >>> input_string2 = "   EXTRA   SPACE   "
             >>> tp.normalize(input_string2)
             'extra space'
    """
    # 모든 단어들은 소문자로 되어야함
    normalized_string = input_string.lower()

    # 띄어쓰기는 한칸으로 되어야함 + 앞뒤 필요없는 띄어쓰기는 제거해야함
    normalized_string = ' '.join(normalized_string.split())

    return normalized_string


def no_vowels(input_string):
    """
    인풋으로 받는 스트링에서 모든 모음 (a, e, i, o, u)를 제거시킨 스트링을 반환함
        Parameters:
            input_string (string): 영어로 된 대문자, 소문자, 띄어쓰기, 문장부호로 이루어진 string
            ex - "This is an example."
        Returns:
            no_vowel_string (string): 모든 모음 (a, e, i, o, u)를 제거시킨 스트링
            ex - "Ths s n xmpl."
        Examples:
            >>> import text_processing as tp
            >>> input_string1 = "This is an example."
            >>> tp.normalize(input_string1)
            "Ths s n xmpl."
            >>> input_string2 = "We love Python!"
            >>> tp.normalize(input_string2)
            ''W lv Pythn!'
    """
    # 모음 대문자, 소문자 제거
    no_vowel_string = ''.join(char for char in input_string if char not in 'aeiouAEIOU')
    return no_vowel_string

-

test model

실제 벡터 dimension ? 볼 수 있음

 

 

 

# model_id, task type 설정
hf_model_id='distilbert-base-uncased-finetuned-sst-2-english'
tm = TransformerModel(model_id=hf_model_id, task_type='text_classification')

es_model_id = tm.elasticsearch_model_id()

# 모델 다운로드
tmp_path = "models"
Path(tmp_path).mkdir(parents=True, exist_ok=True)
model_path, config, vocab_path = tm.save(tmp_path)

# ES 탑재
ptm = PyTorchModel(es, es_model_id)
ptm.import_model(model_path=model_path, config_path=None, vocab_path=vocab_path, config=config)

실습 노트에서 model_id랑 task_type 수정해주고 실행해주면

 

english_model이 deploy된 걸 확인할 수 있음~

 

로컬에 eland깔아서 하면 금방금방 할 수 있음

 

POST _ml/trained_models/distilbert-base-uncased-finetuned-sst-2-english/deployment/_infer #infer 이거 탄다 
{
  "docs": [
    {
      "text_field": "The movie was awesome!"
    }
  ]
}

###############

PUT _ingest/pipeline/sentiment_demo
{
  "processors": [
    {
      "inference": {
        "model_id": "distilbert-base-uncased-finetuned-sst-2-english", #어떤 모델 쓸지
        "field_map": {
          "document": "text_field" #재료, 한글로 되어 있는 리뷰 데이터 쓸거야 
        }
      }
    }
  ],
  "on_failure": [ #실패하면 남기기, 안 남겨도 상관 없으면 제외하기
    {
      "set": {
        "description": "Index document to 'failed-<index>'", #남길 메시지
        "field": "_index",
        "value": "failed-{{{_index}}}"
      }
    },
    {
      "set": {
        "description": "Set error message",
        "field": "ingest.failure",
        "value": "{{_ingest.on_failure_message}}"
      }
    }
  ]
}

##############

POST _reindex
{
  "source": {
    "index": "movie_ratings_vector" #movie_ratings에서 복사해도 되는데~ vector 만들었으니까 써보려고 
  },
  "dest": {
    "index": "movie_ratings_vector_sentiment", 
    "pipeline": "sentiment_demo" #인덱스가 옮겨갈 때 파이프라인 타기
  }
}

#############

GET movie_ratings_vector_sentiment/_search

 

positive 하다고 판단

 

negative 판단

 

0.5 기준에 붙어있어서 p/n 받아버리면 일단 뱉고 보는데 사람 인식하기에

이게 60퍼나 positive라고? 싶을 수 있으니 중립 꼭 적용하기

 

 

-

 

question answering

 

상여 정보/급여 정보/typical 한 문서에서 검색해서 급여 올라가는 정보가 이 문서에 있는지 ~ 같은 

 

-

 

elastic은 슈퍼유저라서 delete도 되니까 유저 id 생성해서 붙기

 

 

-

 

vector image search

https://www.elastic.co/search-labs/blog/articles/finding-your-puppy-with-image-search

 

Finding your puppy with Image Search — Elastic Search Labs

Have you ever been in a situation where you found a lost puppy on the street and didn’t know if it had an owner? Learn how to do it with vector search or image search.

www.elastic.co

이미지 넣은 곳 확인

이미지 넣은 걸로 원래 이미지 중에 일치하는 강아지 찾기

 

-

 

elastic docs chatbot with chatGPT

 

우선 첫 모델 deploy 되어있는지 확인

 

챗봇이 어디든 필요함

특정 사이트에 대해, 특정 기술에 대해 물어보면 답변 잘 못 해주는데 (그 사업의 자산이기도 하고 학습 데이터 맘대로 크롤링 못해서)

그래서 내 데이터를 넣어서 specific한 답변을 주도록 하는 챗봇이 필요함

자연어처리는 활용하되 데이터는 나가는 건 막아놓을 수 있도록

 

streamlit

 

클라우드 계정이... 크롤링이 안되는 경우는 운 안좋게 부정적으로 ~~ 되어서?

 

새 rule 만들기

- 모두 가져온다? x = disallow , rule = regulation expression 

어 밑에꺼랑 반하는 거 아닌가요? -> 위에 있는 거 먼저 실행이라서 ㄱㅊ

 

영어만 가져오는 규칙 추가

 

 

 

POST search-elastic-docs/_mapping
{
"properties": {
 "title-vector": {
   "type": "dense_vector",
   "dims": 768,
   "index": true,
   "similarity": "dot_product"
   }
  }
}

제목만 벡터로 -> title vector

(풀 내용 벡터화 하는 것도 가능하되 오래 걸리겄제)

GET 으로 title-vector 나오는 거 확인 ㄱㄴ

 

title을 재료로 쓸거다~

얘가 얘로 변환되어서 벡터화 할거야~

 

 

 

쌓이고 있는 거 확인

 

데이터 이렇게 쌓이고 있구나~ 확인 

title

 

id는 고유 id

current에 버전 쓸 수 있음

 

이게 title을 벡터화 시킨 것

 

자연어 검색에 boost 24 줌

중요해서

명확도가 낮다, 구어체로 제목이 되어있는 경우에는 이웃을 늘리는 수밖에 없음 / 키워드나 자연어로 정의할 수 없어서

가이드에 명확한 타이틀이 있기 때문에 k를 1으로 두고 검색 (늘려도 됨)

 

how to create index 해당될 때 스코어 올라감

수집된 대상에서 document

 

title만 모아보기

 

이제 chatGPT 연결하기

 

검색 대상은 query_text이다~ 

밑에 model_text랑 같아야 함

 

ES 데이터가 먼저 오고 chatGPT가 그 쿼리에 대해서 딱 그 정보에 대해서만 answer함

만약 검색이 잘 안됐어, 크롤링을 못했어, 그러면 답변하지 말고 negResponse라고 답하고 끝내~

 

 

streamlit -> & 백단에서 실행

 

IP endpoint 복사해서

밑에 URL에 접속해서 IP 주소 입력하기

 

 

그러면 나오는 화면 ↓

한국어로 답변 받는 것

 

내가 한 건

!pip install googletrans==4.0.0-rc1

하고

from googletrans import Translator

def translate_to_korean(text):
    translator = Translator()
    translated_text = translator.translate(text, dest='ko').text
    return translated_text

# 답변 출력
default_response = get_default_response()
if submit_button:
    resp, url = search(query)
    prompt = f"Answer this question in korean: {query}\nUsing only the information from this Elastic Doc: {resp}\nIf the answer is not contained in the supplied doc reply '{negResponse}' and nothing else"
    answer = chat_gpt(prompt)

    # Elasticsearch에서 받아온 답변을 한국어로 번역
    translated_answer = translate_to_korean(answer)

    if default_response in translated_answer:
        st.write(f"ChatGPT: {translated_answer.strip()}")
    else:
        st.write(f"ChatGPT: {translated_answer.strip()}\n\nDocs: {url}")

이렇게 했는데

 

prompt = f"Answer this question in korean: {query}\nUsing only the information from this Elastic Doc: {resp}\nIf the answer is not contained in the supplied doc reply '{negResponse}' and nothing else"

in korean 부분만 추가하면 된다!!

 

그럼 이렇게 됨

근데 한국어로 입력하는 거는 데이터가 다 영어라서 그건 안됨