Oppsummeringer

I denne delen av kurset, skal vi forsøke å bruke språkmodellen på noen artikler. Oppsummeringer av dokumenter kalles gjerne summarizing eller summarization, i koden. Det fins dedikert programvare for å lage oppsummeringer. Imidlertid har store språkmodeller også begynt å beherske oppgaven ganske bra.

Enda en gang bruker vi LangChain, et bibliotek med åpen kildekode, som brukes til å lage programvare med store språkmodeller.

Oppgave 5.1: Lage en ny notebook

Lag en ny Jupyter Notebook som du kaller summarizing ved å klikke i JupyterLabs filmeny, deretter New og Notebook. Hvis du blir spurt om å velge en kjerne, velg “Python 3”. Gi den nye notebooken et navn ved å klikke JupyterLabs filmeny og så Rename Notebook. Bruk navnet summarizing.

Oppgave 5.2: Stoppe gamle kjerner

JupyterLab bruker en Python kjerne til å kjøre kode i hver notebook. For å frigjøre GPU minne som ble brukt i forrige kapittel, bør du stoppe kjernen fra den notebooken. I menyen på venstre side i JupyterLab, klikk den mørke sirkelen som har en hvit firkant inni. Klikk så KERNELS og Shut Down All.

Dokumentenes plassering

Vi har samlet noen forskningsartikler som har Creative Commons lisens. Vi skal nå forsøke å laste opp alle dokumentene fra mappen som defineres under. Hvis du foretrekker, kan du endre stien til en annen mappe:

document_folder = '/fp/projects01/ec443/documents/terrorism'

Språkmodellen

Vi skal bruke modeller fra HuggingFace, en nettside som har verktøy og modeller til maskinlæring. Vi vil bruke språkmodellen google/gemma-3-4b-it, som har åpne vekter og parametere. Modellen har et stort kontekstvindu, som betyr at vi kan bruke den til å behandle ganske store dokumenter. Likevel er den liten nok til at vi kan bruke den med den minste GPUen på Fox.

Tokens kontra ord

Korte ord kan være ett enkelt token, men lengre ord består vanligvis av flere tokens. Maksimum dokumentstørrelse med denne modellen er derfor mindre enn 128k ord. Akkurat hvor mange ord man skal beregne per token kommer an på tokenizeren. Store språkmodeller har vanligvis egne tokenizere. Vi kommer til å bruke standard tokenizeren som hører til den store språkmodellen vi til enhver tid bruker.

import os
os.environ['HF_HOME'] = '/fp/projects01/ec443/huggingface/cache/'

For å bruke modellen, lager vi en pipeline. En pipeline kan bestå av flere steg, men i dette tilfellet trenger vi bare ett steg. Vi kan bruke metoden HuggingFacePipeline.from_model_id(), som automatisk laster ned den spesifiserte modellen fra HuggingFace

Som vi har gjort før, skal vi sjekke om vi har GPU:

import torch
device = 0 if torch.cuda.is_available() else -1
from langchain_community.llms import HuggingFacePipeline

llm = HuggingFacePipeline.from_model_id(
    model_id='google/gemma-3-4b-it',
    task='text-generation',
    device=device,
    pipeline_kwargs={
        'max_new_tokens': 1500,
        #'do_sample': True,
        #'temperature': 0.3,
        #'num_beams': 4,
    }
)

Vi kan gi noen argumenter til “pipelinen”:

  • model_id: modellens navn fra HuggingFace

  • task: oppgaven du planlegger å bruke modellen til

  • device: GPU maskinvaren som enheten bruker. Hvis vi ikke spesifiserer en enhet, vil GPU ikke brukes.

  • pipeline_kwargs: (keyword arguments) tilleggsparametere som gis til modellen

    • max_new_tokens: max lengde på teksten som genereres

    • do_sample: Hvis Falsevil det mest sannsynlige ordet bli valgt. Dette gjør outputten deterministisk. Vi kan sørge for en mer tilfeldig utvelging. Standardverdien later til å være True.

    • temperature: temperaturkontrollen er den statistiske distribusjonen til neste ord. Vanligvis et tall mellom 0 and 1. Lav temperatur øker sannsynligheten for vanlige ord. Høy temperatur øker muligheten for sjeldnere ord i output. Utviklerne har ofte en anbefaling hva angår temperatur. Vi bruker anbefalingen som et startpunkt.

    • num_beams: som standard gir modellen en enkel sekvens av tokens/ord. Med beam search, vil programmet bygge flere samtidige sekvenser, og deretter velge den beste til slutt.

Å lage instruks/ prompt

Vi kan bruke en instruks til å fortelle språkmodellen hvordan vi ønsker at den skal svare. Instruksen bør være kort og konstruktiv. Vi lager også plassholdere til konteksten. Kontekst og input er her det samme. LangChain bytter disse ut med de aktuelle dokumentene når vi kjører en instruks.

from langchain_classic.chains.combine_documents import create_stuff_documents_chain
from langchain_classic.chains.llm import LLMChain
from langchain_core.prompts import PromptTemplate
separator = '\nYour Summary:\n'
prompt_template = '''Write a summary of the following:

{context}
''' + separator
prompt = PromptTemplate(template=prompt_template,
                        input_variables=['context'])

Skille oppsummeringen fra inputten

LangChain returnerer både input instruksen og svaret som genereres i en lang tekst. For å få bare oppsummeringen, må vi splitte oppsummeringen fra dokumentet som vi sendte som input. Til dette kan vi bruke LangChain output parseren som lyder navnet RegexParser:

from langchain_classic.output_parsers import RegexParser
import re

output_parser = RegexParser(
    regex=rf'{separator}(.*)',
    output_keys=['summary'],
    flags=re.DOTALL)

Å lage kjede (chain)

Dokument innlasteren laster hver PDF side som et separat ‘document’. Dette er delvis av tekniske årsaker og på grunn av måten PDFer er organisert. Av denne grunn bruker vi en kjede som kalles create_stuff_documents_chain som (gjen)forener flere dokumenter til ett enkelt stort dokument:

chain = create_stuff_documents_chain(
        llm, prompt, output_parser=output_parser)

Laste inn dokumentene

Vi bruker LangChain sin DirectoryLoader til å laste inn alle filer fra document_folder. document_folder defineres i starten av denne Notebooken:

from langchain_community.document_loaders import DirectoryLoader

loader = DirectoryLoader(document_folder)
documents = loader.load()
print('number of documents:', len(documents))

Lage oppsummeringene

Nå kan vi iterere over disse dokumentene med en for-loop:

summaries = {}

for document in documents:
    filename = document.metadata['source']
    print('Summarizing document:', filename)
    result = chain.invoke({"context": [document]})
    summary = result['summary']
    summaries[filename] = summary
    print('Summary of file', filename)
    print(summary)

Lagre oppsummeringene til tekstfiler

Til slutt lagrer vi oppsummeringene for at vi senere skal kunne se dem. Vi lagrer oppsummeringene i filen summaries.txt. Hvis du vil, kan du lagre hver oppsummering i en egen fil:

with open('summaries.txt', 'w') as outfile:
    for filename in summaries:
        print('Summary of ', filename, file = outfile)
        print(summaries[filename], file=outfile)
        print(file=outfile)

Oppgaver

Oppgave 5.3: Oppsummere dine egne dokumenter

Lag en oppsummering av et dokument som du laster opp i din egen dokumentmappe. Les oppsummeringen nøye, og vurdere resultatet i lys av følgende momenter:

  • Er oppsummeringen nyttig?

  • Er det noe som mangler i oppsummeringen?

  • Er lengden adekvat?

Oppgave 5.4: Tilpass oppsummeringen

Prøv å lage noen tilpasninger i instruksen for å justere oppsummeringen som du fikk i forrige oppgave. Kan du for eksempel spørre etter en lengre eller mer nøyaktig oppsummering? Eller kan du be modellen om å legge vekt på visse aspekter i teksten?

Oppgave 5.5: Lage en oppsummering på et annet språk

Vi kan bruke modellen til å få en oppsummering på et annet språk enn originaldokumentet. Hvis for eksempel instruksen er på Norsk, vil svaret vanligvis også gis på Norsk. Du kan også spesifisere i instruksen hvilket språk du ønsker å ha oppsummeringen på. Bruk modellen til å lage en oppsummering av ditt dokument fra den andre oppgaven, på et annet språk enn det opprinnelig ble gitt.

Anbefaling : Dersom du skal feilsøke pythonfilene dine, kan det være hensiktsmessig å laste ned Sublime text.

Bonusmateriale