1_Extraction

Jacob Kim·2024년 2월 1일
0

Naver Project Week5

목록 보기
2/12

Use case

Raw LLM 생성에서 구조화된 출력을 얻는 것은 어렵습니다.

예를 들어, 특정 스키마로 포맷된 모델 출력이 필요하다고 가정해 보겠습니다:

  • 데이터베이스에 삽입할 구조화된 행을 추출하는 경우
  • API 매개변수 추출
  • 사용자 쿼리의 다른 부분 추출(예: 시맨틱 검색과 키워드 검색)

Overview

이를 위한 두 가지 주요 접근 방식이 있습니다:

  • Functions: 일부 LLM은 functions를 호출하여 LLM 응답에서 임의의 엔티티를 추출할 수 있습니다.

  • Parsing: 출력 구문 분석기는 LLM 응답을 구조화하는 클래스입니다.

일부 LLM만 함수(예: OpenAI)를 지원하며, 파서보다 더 일반적입니다.

구문 분석기는 제공된 스키마에 열거된 내용(예: 사람의 특정 속성)을 정확하게 추출합니다.

함수는 제공된 스키마 이외의 것(예: 사용자가 요청하지 않은 사람에 대한 속성)을 추론할 수 있습니다.

Quickstart

OpenAI 함수는 추출을 시작하는 한 가지 방법입니다.

LLM 출력에서 추출할 속성을 지정하는 스키마를 정의합니다.

그런 다음 create_extraction_chain을 사용하여 OpenAI 함수 호출을 통해 원하는 스키마를 추출할 수 있습니다.

!pip install langchain openai
Collecting langchain
  Downloading langchain-0.1.3-py3-none-any.whl (803 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 803.6/803.6 kB 5.5 MB/s eta 0:00:00
Collecting openai
  Downloading openai-1.9.0-py3-none-any.whl (223 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 223.4/223.4 kB 5.3 MB/s eta 0:00:00
import getpass
import os

os.environ["OPENAI_API_KEY"] = getpass.getpass() #"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"
from langchain.chains import create_extraction_chain
from langchain.chat_models import ChatOpenAI

# Schema
schema = {
    "properties": {
        "name": {"type": "string"},
        "height": {"type": "integer"},
        "hair_color": {"type": "string"},
    },
    "required": ["name", "height"],
}

# Input
inp = """Alex is 5 feet tall. Claudia is 1 feet taller Alex and jumps higher than him. Claudia is a brunette and Alex is blonde."""

# Run chain
llm = ChatOpenAI(temperature=0.7, model="gpt-3.5-turbo")
chain = create_extraction_chain(schema, llm)
chain.run(inp)
/usr/local/lib/python3.10/dist-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The class `langchain_community.chat_models.openai.ChatOpenAI` was deprecated in langchain-community 0.0.10 and will be removed in 0.2.0. An updated version of the class exists in the langchain-openai package and should be used instead. To use it run `pip install -U langchain-openai` and import as `from langchain_openai import ChatOpenAI`.
  warn_deprecated(
/usr/local/lib/python3.10/dist-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The function `run` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.
  warn_deprecated(
[{'name': 'Alex', 'height': 5, 'hair_color': 'blonde'},
 {'name': 'Claudia', 'height': 6, 'hair_color': 'brunette'}]

Option 1: OpenAI functions

Looking under the hood

create_extraction_chain 호출할 때 어떤 일이 일어나는지 자세히 살펴봅시다.

이 정보_추출 함수는 여기에 정의되어 있으며 딕셔너리를 반환합니다.

모델 출력에서 dict를 볼 수 있습니다:

 {
      "info": [
        {
          "name": "Alex",
          "height": 5,
          "hair_color": "blonde"
        },
        {
          "name": "Claudia",
          "height": 6,
          "hair_color": "brunette"
        }
      ]
    }

그런 다음 create_extraction_chainJsonKeyOutputFunctionsParser를 사용하여 원시 LLM 출력을 파싱합니다.
그 결과 위의 체인에서 반환된 JSON 객체 목록이 생성됩니다:

[{'name': 'Alex', 'height': 5, 'hair_color': 'blonde'},
 {'name': 'Claudia', 'height': 6, 'hair_color': 'brunette'}]

Multiple entity types

이를 더 확장할 수 있습니다.
개와 사람을 구분하고 싶다고 가정해 봅시다.
각 속성에 person_dog_ 접두사를 추가하면 됩니다.

schema = {
    "properties": {
        "person_name": {"type": "string"},
        "person_height": {"type": "integer"},
        "person_hair_color": {"type": "string"},
        "dog_name": {"type": "string"},
        "dog_breed": {"type": "string"},
    },
    "required": ["person_name", "person_height"],
}

chain = create_extraction_chain(schema, llm)

inp = """Alex is 5 feet tall. Claudia is 1 feet taller Alex and jumps higher than him. Claudia is a brunette and Alex is blonde.
Alex's dog Frosty is a labrador and likes to play hide and seek."""

chain.run(inp)
[{'person_name': 'Alex',
  'person_height': 5,
  'person_hair_color': 'blonde',
  'dog_name': 'Frosty',
  'dog_breed': 'labrador'},
 {'person_name': 'Claudia',
  'person_height': 6,
  'person_hair_color': 'brunette'}]

Unrelated entities

필수: []를 사용하면 모델이 단일 엔티티(사람 또는 개)에 대해 사람 속성만 또는 개 속성만을 반환하도록 허용합니다.

schema = {
    "properties": {
        "person_name": {"type": "string"},
        "person_height": {"type": "integer"},
        "person_hair_color": {"type": "string"},
        "dog_name": {"type": "string"},
        "dog_breed": {"type": "string"},
    },
    "required": [],
}

chain = create_extraction_chain(schema, llm)

inp = """Alex is 5 feet tall. Claudia is 1 feet taller Alex and jumps higher than him. Claudia is a brunette and Alex is blonde.
Willow is a German Shepherd that likes to play with other dogs and can always be found playing with Milo, a border collie that lives close by."""

chain.run(inp)
[{'person_name': 'Alex', 'person_height': 5, 'person_hair_color': 'blonde'},
 {'person_name': 'Claudia',
  'person_height': 6,
  'person_hair_color': 'brunette'},
 {'dog_name': 'Willow', 'dog_breed': 'German Shepherd'},
 {'dog_name': 'Milo', 'dog_breed': 'Border Collie'}]

Extra information

The power of functions (파서만 사용할 때와 비교했을 때) 의미 추출을 수행할 수 있는 능력에 있습니다.

특히 '스키마에 명시적으로 열거되지 않은 것을 요청할 수 있다'는 점입니다.

개에 대한 지정되지 않은 추가 정보를 원한다고 가정해 봅시다.

비정형 추출을 위한 자리 표시자인 dog_extra_info를 추가할 수 있습니다.

schema = {
    "properties": {
        "person_name": {"type": "string"},
        "person_height": {"type": "integer"},
        "person_hair_color": {"type": "string"},
        "dog_name": {"type": "string"},
        "dog_breed": {"type": "string"},
        "dog_extra_info": {"type": "string"},
    },
}

chain = create_extraction_chain(schema, llm)
chain.run(inp)
[{'person_name': 'Alex', 'person_height': 5, 'person_hair_color': 'blonde'},
 {'person_name': 'Claudia',
  'person_height': 6,
  'person_hair_color': 'brunette'},
 {'dog_name': 'Willow',
  'dog_breed': 'German Shepherd',
  'dog_extra_info': 'likes to play with other dogs'},
 {'dog_name': 'Milo',
  'dog_breed': 'border collie',
  'dog_extra_info': 'lives close by'}]

이를 통해 개에 대한 추가 정보를 얻을 수 있습니다.

Pydantic

Pydantic은 Python용 데이터 유효성 검사 및 설정 관리 라이브러리입니다.

이 라이브러리를 사용하면 객체를 인스턴스화할 때 자동으로 유효성이 검사되는 속성을 가진 데이터 클래스를 만들 수 있습니다.

유형으로 주석이 달린 속성을 가진 클래스를 정의할 수 있습니다.

from typing import Optional

from langchain.chains import create_extraction_chain_pydantic
from langchain.pydantic_v1 import BaseModel


# Pydantic data class
class Properties(BaseModel):
    person_name: str
    person_height: int
    person_hair_color: str
    dog_breed: Optional[str]
    dog_name: Optional[str]


# Extraction
chain = create_extraction_chain_pydantic(pydantic_schema=Properties, llm=llm)

# Run
inp = """Alex is 5 feet tall. Claudia is 1 feet taller Alex and jumps higher than him. Claudia is a brunette and Alex is blonde."""
chain.run(inp)
[Properties(person_name='Alex', person_height=5, person_hair_color='blonde', dog_breed=None, dog_name=None),
 Properties(person_name='Claudia', person_height=6, person_hair_color='brunette', dog_breed=None, dog_name=None)]

trace에서 볼 수 있듯이, 위와 같이 Pydantic 스키마와 함께 information_extraction 함수를 사용합니다.

옵션 2: 파싱

출력 구문 분석기는 언어 모델 응답을 구조화하는 데 도움이 되는 클래스입니다.

위에 표시된 것처럼 create_extraction_chain에서 OpenAI 함수 호출의 출력을 구문 분석하는 데 사용됩니다.

하지만 함수와 독립적으로 사용할 수도 있습니다.

Pydantic

위와 마찬가지로 Pydantic 데이터 클래스를 기반으로 한 세대를 파싱해 보겠습니다.

from typing import Optional, Sequence

from langchain.llms import OpenAI
from langchain.output_parsers import PydanticOutputParser
from langchain.prompts import (
    PromptTemplate,
)
from pydantic import BaseModel, Field, validator


class Person(BaseModel):
    person_name: str
    person_height: int
    person_hair_color: str
    dog_breed: Optional[str]
    dog_name: Optional[str]


class People(BaseModel):
    """Identifying information about all people in a text."""

    people: Sequence[Person]


# Run
query = """Alex is 5 feet tall. Claudia is 1 feet taller Alex and jumps higher than him. Claudia is a brunette and Alex is blonde."""

# Set up a parser + inject instructions into the prompt template.
parser = PydanticOutputParser(pydantic_object=People)
# print(parser.get_format_instructions())

# Prompt
prompt = PromptTemplate(
    template="Answer the user query.\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    partial_variables={"format_instructions": parser.get_format_instructions()},
)

# Run
_input = prompt.format_prompt(query=query)
# print(_input)
model = OpenAI(temperature=0)
output = model(_input.to_string())
parser.parse(output)
/usr/local/lib/python3.10/dist-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The class `langchain_community.llms.openai.OpenAI` was deprecated in langchain-community 0.0.10 and will be removed in 0.2.0. An updated version of the class exists in the langchain-openai package and should be used instead. To use it run `pip install -U langchain-openai` and import as `from langchain_openai import OpenAI`.
  warn_deprecated(
/usr/local/lib/python3.10/dist-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.7 and will be removed in 0.2.0. Use invoke instead.
  warn_deprecated(
People(people=[Person(person_name='Alex', person_height=5, person_hair_color='blonde', dog_breed=None, dog_name=None), Person(person_name='Claudia', person_height=6, person_hair_color='brunette', dog_breed=None, dog_name=None)])

LLM이 원하는 형식으로 출력하도록하기 위해 투샷 프롬프트를 제공합니다.

그리고 조금 더 작업해야 합니다:

  • Person의 여러 인스턴스를 보유하는 클래스를 정의합니다.
  • LLM의 출력을 Pydantic 클래스로 명시적으로 파싱합니다.

다른 경우에도 이와 같은 결과를 볼 수 있습니다.

from langchain.llms import OpenAI
from langchain.output_parsers import PydanticOutputParser
from langchain.prompts import (
    PromptTemplate,
)
from pydantic import BaseModel, Field, validator


# Define your desired data structure.
class Joke(BaseModel):
    setup: str = Field(description="question to set up a joke")
    punchline: str = Field(description="answer to resolve the joke")

    # You can add custom validation logic easily with Pydantic.
    @validator("setup")
    def question_ends_with_question_mark(cls, field):
        if field[-1] != "?":
            raise ValueError("Badly formed question!")
        return field


# And a query intended to prompt a language model to populate the data structure.
joke_query = "Tell me a joke."

# Set up a parser + inject instructions into the prompt template.
parser = PydanticOutputParser(pydantic_object=Joke)
print(parser.get_format_instructions())

# Prompt
prompt = PromptTemplate(
    template="Answer the user query.\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    partial_variables={"format_instructions": parser.get_format_instructions()},
)

# Run
_input = prompt.format_prompt(query=joke_query)
print(_input)
model = OpenAI(temperature=0)
output = model(_input.to_string())
parser.parse(output)
The output should be formatted as a JSON instance that conforms to the JSON schema below.

As an example, for the schema {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]}
the object {"foo": ["bar", "baz"]} is a well-formatted instance of the schema. The object {"properties": {"foo": ["bar", "baz"]}} is not well-formatted.

Here is the output schema:

{"properties": {"setup": {"title": "Setup", "description": "question to set up a joke", "type": "string"}, "punchline": {"title": "Punchline", "description": "answer to resolve the joke", "type": "string"}}, "required": ["setup", "punchline"]}

text='Answer the user query.\nThe output should be formatted as a JSON instance that conforms to the JSON schema below.\n\nAs an example, for the schema {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]}\nthe object {"foo": ["bar", "baz"]} is a well-formatted instance of the schema. The object {"properties": {"foo": ["bar", "baz"]}} is not well-formatted.\n\nHere is the output schema:\n```\n{"properties": {"setup": {"title": "Setup", "description": "question to set up a joke", "type": "string"}, "punchline": {"title": "Punchline", "description": "answer to resolve the joke", "type": "string"}}, "required": ["setup", "punchline"]}\n```\nTell me a joke.\n'
Joke(setup='Why did the tomato turn red?', punchline='Because it saw the salad dressing!')

보시다시피, 우리가 원래 원했던 스키마를 따르는 Joke 클래스의 출력을 얻습니다: setup, punchline.

연습문제

1. 기본 추출 연습

  • 주어진 텍스트에서 언급된 사람들의 이름과 나이를 추출하는 파이썬 스크립트를 작성합니다.
  1. 스키마 정의: 사람의 이름과 나이를 추출하기 위한 스키마를 정의합니다. 이 스키마는 이름을 문자열로, 나이를 정수로 갖는 구조를 가집니다.
  2. 텍스트 입력: 추출할 정보를 포함하는 샘플 텍스트를 준비합니다.
  3. 추출 체인 생성: langchain 라이브러리를 사용하여 추출 체인을 생성합니다. 이 체인은 입력된 텍스트에서 이름과 나이 정보를 추출합니다.
  4. 추출 실행 및 결과 확인: 생성된 체인을 실행하여 텍스트에서 정보를 추출하고, 결과를 확인합니다.
# 스키마 정의
schema = {
    "properties": {
        "name": {"type": "string"},
        "age": {"type": "integer"}
    },
    "required": ["name", "age"]
}

# 텍스트 입력
input_text = "John is 30 years old. Mary is two years younger than John."

# 추출 체인 생성
llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo")
chain = create_extraction_chain(schema, llm)

# 추출 실행
result = chain.run(input_text)

# 결과 출력
print(result)
# [{'name': 'John', 'age': 30}, {'name': 'Mary', 'age': 28}]
# from langchain.chains import create_extraction_chain_pydantic

class Properties(BaseModel):
    name: str
    age: int

# 텍스트 입력
input_text = "John is 30 years old. Mary is two years younger than John."

# 추출 체인 생성
llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo")
chain = create_extraction_chain_pydantic(pydantic_schema=Properties, llm=llm)

# 추출 실행
result = chain.run(input_text)

# 결과 출력
print(result)
# [Properties(name='John', age=30), Properties(name='Mary', age=28)]

2. 복잡한 쿼리 처리

  • 서술 문단에서 이벤트에 대한 자세한 정보(예: 이벤트 이름, 날짜, 위치, 참가자)를 추출하는 스크립트를 작성합니다.
  1. 이벤트 정보 스키마 정의: 이벤트 이름, 날짜, 위치, 참가자 등의 정보를 포함하는 스키마를 정의합니다.
  2. 텍스트 입력: 추출할 정보를 포함하는 복잡한 서술 문단을 준비합니다
  3. 추출 체인 생성 및 실행: langchain 라이브러리를 사용하여 추출 체인을 생성하고, 준비된 텍스트에서 정보를 추출합니다.
  4. 결과 확인 및 분석: 추출된 결과를 확인하고, 스크립트가 복잡한 쿼리를 어떻게 처리하는지 분석합니다.
schema = {
    "properties": {
        "event_name": {"type": "string"},
        "date": {"type": "string"}, # 날짜검증이 필요할 수도
        "location": {"type": "string"},
        "participants": {"type": "array", "items":{"type": "string"}},
    },
    "required": ["event_name", "date", "location"]
}

# 텍스트 입력
input_text = "The annual science fair, which will be held on March 15th at the city hall, is expected to attract many notable scientists including Dr. Jane Doe and Prof. John Smith."

# 추출 체인 생성
llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo")
chain = create_extraction_chain(schema, llm)

# 추출 실행
result = chain.run(input_text)

# 결과 출력
print(result)
#[{'event_name': 'annual science fair', 'date': 'March 15th', 'location': 'city hall', 'participants': ['Dr. Jane Doe', 'Prof. John Smith']}]
class Event(BaseModel):
    event_name: str
    date: str
    location: str
    participants: list

query = "The annual science fair, which will be held on March 15th at the city hall, is expected to attract many notable scientists including Dr. Jane Doe and Prof. John Smith."

# Set Parser
parser = PydanticOutputParser(pydantic_object=Event)

# Prompt template
prompt = PromptTemplate(
    template="Answer the query. \n {format_instructions} \m {query} \n",
    input_variables=["query"],
    partial_variables={"format_instructions": parser.get_format_instructions()}
)

# run chain
_input = prompt.format_prompt(query=query)
model = OpenAI(temperature=0)
outputs = model(_input.to_string())
parser.parse(outputs)
#Event(event_name='Annual Science Fair', date='March 15th', location='City Hall', participants=['Dr. Jane Doe', 'Prof. John Smith'])
class Event(BaseModel):
    name: str
    event_name: str
    date: str
    location: str

class People(BaseModel):
    """Event Participants"""
    participants: Sequence[Event]

query = "The annual science fair, which will be held on March 15th at the city hall, is expected to attract many notable scientists including Dr. Jane Doe and Prof. John Smith."

# Set Parser
parser = PydanticOutputParser(pydantic_object=People)

# Prompt template
prompt = PromptTemplate(
    template="Answer the query. \n {format_instructions} \m {query} \n",
    input_variables=["query"],
    partial_variables={"format_instructions": parser.get_format_instructions()}
)

# run chain
_input = prompt.format_prompt(query=query)
model = OpenAI(temperature=0)
outputs = model(_input.to_string())
parser.parse(outputs)
#People(participants=[Event(name='Dr. Jane Doe', event_name='Annual 
#Science Fair', date='March 15th', location='City Hall'), Event(name='Prof. John Smith', event_name='Annual Science Fair', date='March 15th', location='City Hall')])

동적 스키마 생성

  • 사용자 입력에 기반하여 스키마를 자동으로 생성하는 파이썬 스크립트를 작성합니다. 이 스크립트는 사용자가 원하는 정보 유형을 입력하면 해당하는 스키마를 생성하고, 언어 모델을 사용하여 이 스키마에 따라 텍스트에서 정보를 추출합니다.
  1. 사용자 입력 처리: 사용자가 추출하고자 하는 정보 유형을 입력할 수 있는 인터페이스를 구현합니다. 예를 들어, 사용자가 "이름", "나이", "직업" 등을 입력할 수 있습니다.
  2. 동적 스키마 생성: 사용자의 입력을 기반으로 동적으로 스키마를 생성하는 함수를 구현합니다. 이 함수는 사용자가 입력한 정보 유형에 따라 적절한 스키마를 만듭니다.
  3. 추출 체인 설정 및 실행: 생성된 스키마를 사용하여 langchain 라이브러리의 추출 체인을 설정하고 실행합니다.
  4. 결과 출력 및 검증: 추출된 결과를 출력하고, 스크립트가 정확하게 사용자 요구 사항에 맞는 스키마를 생성했는지 검증합니다.
def create_dynamic_schema(user_inputs):
    schema = {"properties": {}, "required": []}
    for input in user_inputs:
        # 여기에서 각 입력 유형에 대한 스키마 정의를 추가합니다.
        schema["properties"][input] = {"type": "string"}  # 예시: 모든 필드를 문자열로 처리
        schema["required"].append(input)
    return schema

# 사용자 입력
user_inputs = ["name", "age"]  # 예시: 사용자가 입력한 필드

# 스키마 생성
dynamic_schema = create_dynamic_schema(user_inputs)

# 텍스트 입력
input_text = "John, a 30-year-old engineer, works at Acme Corp."

# 추출 체인 생성
llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo")
chain = create_extraction_chain(dynamic_schema, llm)

# 추출 실행
result = chain.run(input_text)

# 결과 출력
print(result)
# [{'name': 'John', 'age': '30'}, {'name': 'engineer'}, {'name': 'Acme Corp.'}]
profile
AI, Information and Communication, Electronics, Computer Science, Bio, Algorithms

0개의 댓글