Kunstig intelligens
Designmønstre i Python for AI- og LLM-ingeniører: En praktisk guide

Som AI-ingeniører er det avgjørende å lage ren, effektiv og vedlikeholdbar kode, spesielt når du bygger komplekse systemer.
Design mønstre er gjenbrukbare løsninger på vanlige problemer innen programvaredesign. Til AI og store språkmodeller (LLM) ingeniører, hjelper designmønstre å bygge robuste, skalerbare og vedlikeholdbare systemer som håndterer komplekse arbeidsflyter effektivt. Denne artikkelen dykker ned i designmønstre i Python, med fokus på deres relevans i AI og LLM-baserte systemer. Jeg vil forklare hvert mønster med praktiske AI-brukstilfeller og Python-kodeeksempler.
La oss utforske noen viktige designmønstre som er spesielt nyttige i AI- og maskinlæringssammenhenger, sammen med Python-eksempler.
Hvorfor designmønstre er viktige for AI-ingeniører
AI-systemer involverer ofte:
- Kompleks objektoppretting (f.eks. lasting av modeller, dataforbehandlingsrørledninger).
- Administrere interaksjoner mellom komponenter (f.eks. modellslutning, sanntidsoppdateringer).
- Håndtere skalerbarhet, vedlikehold og fleksibilitet for endrede krav.
Designmønstre løser disse utfordringene, gir en klar struktur og reduserer ad-hoc-fikser. De faller inn i tre hovedkategorier:
- Kreasjonelle mønstre: Fokus på objektskaping. (Singleton, Factory, Builder)
- Strukturelle mønstre: Organiser relasjonene mellom objekter. (adapter, dekoratør)
- Atferdsmønstre: Administrer kommunikasjon mellom objekter. (Strategi, observatør)
1. Singleton-mønster
Ocuco Singleton mønster sikrer at en klasse bare har én forekomst og gir et globalt tilgangspunkt til den forekomsten. Dette er spesielt verdifullt i AI-arbeidsflyter der delte ressurser – som konfigurasjonsinnstillinger, loggingssystemer eller modellforekomster – må administreres konsekvent uten redundans.
Når skal du bruke
- Administrere globale konfigurasjoner (f.eks. modellhyperparametere).
- Dele ressurser på tvers av flere tråder eller prosesser (f.eks. GPU-minne).
- Sikre konsekvent tilgang til en enkelt inferensmotor eller databasetilkobling.
Gjennomføring
Slik implementerer du et Singleton-mønster i Python for å administrere konfigurasjoner for en AI-modell:
class ModelConfig: """ A Singleton class for managing global model configurations. """ _instance = None # Class variable to store the singleton instance def __new__(cls, *args, **kwargs): if not cls._instance: # Create a new instance if none exists cls._instance = super().__new__(cls) cls._instance.settings = {} # Initialize configuration dictionary return cls._instance def set(self, key, value): """ Set a configuration key-value pair. """ self.settings[key] = value def get(self, key): """ Get a configuration value by key. """ return self.settings.get(key) # Usage Example config1 = ModelConfig() config1.set("model_name", "GPT-4") config1.set("batch_size", 32) # Accessing the same instance config2 = ModelConfig() print(config2.get("model_name")) # Output: GPT-4 print(config2.get("batch_size")) # Output: 32 print(config1 is config2) # Output: True (both are the same instance)
Forklaring
- Ocuco
__new__
Metode: Dette sikrer at bare én forekomst av klassen opprettes. Hvis en forekomst allerede eksisterer, returnerer den den eksisterende. - Delt tilstand: Både
config1
ogconfig2
peke på samme instans, noe som gjør alle konfigurasjoner globalt tilgjengelige og konsistente. - AI Use Case: Bruk dette mønsteret til å administrere globale innstillinger som stier til datasett, loggkonfigurasjoner eller miljøvariabler.
2. Fabrikkmønster
Ocuco Fabrikkmønster gir en måte å delegere opprettelsen av objekter til underklasser eller dedikerte fabrikkmetoder. I AI-systemer er dette mønsteret ideelt for å lage forskjellige typer modeller, datalastere eller rørledninger dynamisk basert på kontekst.
Når skal du bruke
- Dynamisk lage modeller basert på brukerinnspill eller oppgavekrav.
- Administrere kompleks objektopprettingslogikk (f.eks. flertrinns forbehandlingsrørledninger).
- Frakopling av objektforekomst fra resten av systemet for å forbedre fleksibiliteten.
Gjennomføring
La oss bygge en fabrikk for å lage modeller for forskjellige AI-oppgaver, som tekstklassifisering, oppsummering og oversettelse:
class BaseModel: """ Abstract base class for AI models. """ def predict(self, data): raise NotImplementedError("Subclasses must implement the `predict` method") class TextClassificationModel(BaseModel): def predict(self, data): return f"Classifying text: {data}" class SummarizationModel(BaseModel): def predict(self, data): return f"Summarizing text: {data}" class TranslationModel(BaseModel): def predict(self, data): return f"Translating text: {data}" class ModelFactory: """ Factory class to create AI models dynamically. """ @staticmethod def create_model(task_type): """ Factory method to create models based on the task type. """ task_mapping = { "classification": TextClassificationModel, "summarization": SummarizationModel, "translation": TranslationModel, } model_class = task_mapping.get(task_type) if not model_class: raise ValueError(f"Unknown task type: {task_type}") return model_class() # Usage Example task = "classification" model = ModelFactory.create_model(task) print(model.predict("AI will transform the world!")) # Output: Classifying text: AI will transform the world!
Forklaring
- Abstrakt grunnklasse: The
BaseModel
klasse definerer grensesnittet (predict
) som alle underklasser må implementere, for å sikre konsistens. - Fabrikklogikk: The
ModelFactory
velger dynamisk riktig klasse basert på oppgavetypen og oppretter en forekomst. - utvidelses~~POS=TRUNC: Det er enkelt å legge til en ny modelltype – bare implementer en ny underklasse og oppdater fabrikkens
task_mapping
.
AI Use Case
Tenk deg at du designer et system som velger en annen LLM (f.eks. BERT, GPT eller T5) basert på oppgaven. Fabrikkmønsteret gjør det enkelt å utvide systemet etter hvert som nye modeller blir tilgjengelige uten å endre eksisterende kode.
3. Byggermønster
Ocuco Builder mønster skiller konstruksjonen av et komplekst objekt fra dets representasjon. Det er nyttig når et objekt involverer flere trinn for å initialisere eller konfigurere.
Når skal du bruke
- Bygge flertrinns rørledninger (f.eks. dataforbehandling).
- Administrere konfigurasjoner for eksperimenter eller modelltrening.
- Lage objekter som krever mange parametere, sikre lesbarhet og vedlikehold.
Gjennomføring
Slik bruker du Builder-mønsteret til å lage en dataforbehandlingspipeline:
class DataPipeline: """ Builder class for constructing a data preprocessing pipeline. """ def __init__(self): self.steps = [] def add_step(self, step_function): """ Add a preprocessing step to the pipeline. """ self.steps.append(step_function) return self # Return self to enable method chaining def run(self, data): """ Execute all steps in the pipeline. """ for step in self.steps: data = step(data) return data # Usage Example pipeline = DataPipeline() pipeline.add_step(lambda x: x.strip()) # Step 1: Strip whitespace pipeline.add_step(lambda x: x.lower()) # Step 2: Convert to lowercase pipeline.add_step(lambda x: x.replace(".", "")) # Step 3: Remove periods processed_data = pipeline.run(" Hello World. ") print(processed_data) # Output: hello world
Forklaring
- Kjede metoder: The
add_step
metoden tillater kjeding for en intuitiv og kompakt syntaks når du definerer pipelines. - Trinn-for-trinn utførelse: Rørledningen behandler data ved å kjøre den gjennom hvert trinn i rekkefølge.
- AI Use Case: Bruk Builder-mønsteret til å lage komplekse, gjenbrukbare dataforbehandlingspipelines eller modelltreningsoppsett.
4. Strategimønster
Ocuco Strategimønster definerer en familie av utskiftbare algoritmer, som innkapsler hver og en og lar atferden endre seg dynamisk under kjøring. Dette er spesielt nyttig i AI-systemer der den samme prosessen (f.eks. inferens eller databehandling) kan kreve forskjellige tilnærminger avhengig av konteksten.
Når skal du bruke
- Bytte mellom ulike slutning strategier (f.eks. batchbehandling vs. streaming).
- Å bruke forskjellige databehandlingsteknikker dynamisk.
- Velge ressursstyringsstrategier basert på tilgjengelig infrastruktur.
Gjennomføring
La oss bruke strategimønsteret til å implementere to forskjellige inferensstrategier for en AI-modell: batch-inferens og streaming-inferens.
class InferenceStrategy: """ Abstract base class for inference strategies. """ def infer(self, model, data): raise NotImplementedError("Subclasses must implement the `infer` method") class BatchInference(InferenceStrategy): """ Strategy for batch inference. """ def infer(self, model, data): print("Performing batch inference...") return [model.predict(item) for item in data] class StreamInference(InferenceStrategy): """ Strategy for streaming inference. """ def infer(self, model, data): print("Performing streaming inference...") results = [] for item in data: results.append(model.predict(item)) return results class InferenceContext: """ Context class to switch between inference strategies dynamically. """ def __init__(self, strategy: InferenceStrategy): self.strategy = strategy def set_strategy(self, strategy: InferenceStrategy): """ Change the inference strategy dynamically. """ self.strategy = strategy def infer(self, model, data): """ Delegate inference to the selected strategy. """ return self.strategy.infer(model, data) # Mock Model Class class MockModel: def predict(self, input_data): return f"Predicted: {input_data}" # Usage Example model = MockModel() data = ["sample1", "sample2", "sample3"] context = InferenceContext(BatchInference()) print(context.infer(model, data)) # Output: # Performing batch inference... # ['Predicted: sample1', 'Predicted: sample2', 'Predicted: sample3'] # Switch to streaming inference context.set_strategy(StreamInference()) print(context.infer(model, data)) # Output: # Performing streaming inference... # ['Predicted: sample1', 'Predicted: sample2', 'Predicted: sample3']
Forklaring
- Abstrakt strategiklasse: The
InferenceStrategy
definerer grensesnittet som alle strategier må følge. - Konkrete strategier: Hver strategi (f.eks.
BatchInference
,StreamInference
) implementerer logikken som er spesifikk for den tilnærmingen. - Dynamisk veksling: The
InferenceContext
tillater byttestrategier under kjøring, og tilbyr fleksibilitet for forskjellige brukstilfeller.
Når skal du bruke
- Bytt mellom batch-slutning for offline-behandling og strømmeslutning for sanntidsapplikasjoner.
- Juster dynamisk dataforsterkning eller forbehandlingsteknikker basert på oppgaven eller inndataformatet.
5. Observatørmønster
Ocuco Observatørmønster etablerer et en-til-mange forhold mellom objekter. Når ett objekt (subjektet) endrer tilstand, blir alle dets pårørende (observatører) automatisk varslet. Dette er spesielt nyttig i AI-systemer for sanntidsovervåking, hendelseshåndtering eller datasynkronisering.
Når skal du bruke
- Overvåking av beregninger som nøyaktighet eller tap under modelltrening.
- Sanntidsoppdateringer for dashbord eller logger.
- Håndtere avhengigheter mellom komponenter i komplekse arbeidsflyter.
Gjennomføring
La oss bruke Observer Pattern til å overvåke ytelsen til en AI-modell i sanntid.
class Subject: """ Base class for subjects being observed. """ def __init__(self): self._observers = [] def attach(self, observer): """ Attach an observer to the subject. """ self._observers.append(observer) def detach(self, observer): """ Detach an observer from the subject. """ self._observers.remove(observer) def notify(self, data): """ Notify all observers of a change in state. """ for observer in self._observers: observer.update(data) class ModelMonitor(Subject): """ Subject that monitors model performance metrics. """ def update_metrics(self, metric_name, value): """ Simulate updating a performance metric and notifying observers. """ print(f"Updated {metric_name}: {value}") self.notify({metric_name: value}) class Observer: """ Base class for observers. """ def update(self, data): raise NotImplementedError("Subclasses must implement the `update` method") class LoggerObserver(Observer): """ Observer to log metrics. """ def update(self, data): print(f"Logging metric: {data}") class AlertObserver(Observer): """ Observer to raise alerts if thresholds are breached. """ def __init__(self, threshold): self.threshold = threshold def update(self, data): for metric, value in data.items(): if value > self.threshold: print(f"ALERT: {metric} exceeded threshold with value {value}") # Usage Example monitor = ModelMonitor() logger = LoggerObserver() alert = AlertObserver(threshold=90) monitor.attach(logger) monitor.attach(alert) # Simulate metric updates monitor.update_metrics("accuracy", 85) # Logs the metric monitor.update_metrics("accuracy", 95) # Logs and triggers alert
- Emne: Administrerer en liste over observatører og varsler dem når tilstanden endres. I dette eksemplet er
ModelMonitor
klassespor beregninger. - observatører: Utfør spesifikke handlinger når du blir varslet. For eksempel
LoggerObserver
logger beregninger, mensAlertObserver
varsler hvis en terskel overskrides. - Frakoblet design: Observatører og forsøkspersoner er løst koblet, noe som gjør systemet modulært og utvidbart.
Hvordan designmønstre er forskjellige for AI-ingeniører vs. tradisjonelle ingeniører
Designmønstre, selv om de er universelt anvendelige, får unike egenskaper når de implementeres i AI-teknikk sammenlignet med tradisjonell programvareteknikk. Forskjellen ligger i utfordringene, målene og arbeidsflytene som er iboende til AI-systemer, som ofte krever at mønstre tilpasses eller utvides utover deres konvensjonelle bruk.
1. Objektskaping: Statiske vs. dynamiske behov
- Tradisjonell ingeniørfag: Objektopprettingsmønstre som Factory eller Singleton brukes ofte til å administrere konfigurasjoner, databasetilkoblinger eller brukersesjonstilstander. Disse er generelt statiske og veldefinerte under systemdesign.
- AI Engineering: Objektskaping involverer ofte dynamiske arbeidsflyter, Slik som:
- Opprette modeller på farten basert på brukerinndata eller systemkrav.
- Laster forskjellige modellkonfigurasjoner for oppgaver som oversettelse, oppsummering eller klassifisering.
- Instantiering av flere databehandlingsrørledninger som varierer etter datasetts egenskaper (f.eks. tabell vs. ustrukturert tekst).
Eksempel: I AI kan et fabrikkmønster dynamisk generere en dyp læringsmodell basert på oppgavetypen og maskinvarebegrensninger, mens det i tradisjonelle systemer ganske enkelt kan generere en brukergrensesnittkomponent.
2. Ytelsesbegrensninger
- Tradisjonell ingeniørfag: Designmønstre er vanligvis optimalisert for ventetid og gjennomstrømning i applikasjoner som webservere, databasespørringer eller UI-gjengivelse.
- AI Engineering: Ytelseskrav i AI strekker seg til modellslutningsforsinkelse, GPU/TPU utnyttelse, og minneoptimalisering. Mønstrene må romme:
- Bufre mellomresultater for å redusere redundante beregninger (dekorator- eller proxy-mønstre).
- Bytte algoritmer dynamisk (strategimønster) for å balansere ventetid og nøyaktighet basert på systembelastning eller sanntidsbegrensninger.
3. Datasentrisk natur
- Tradisjonell ingeniørfag: Mønstre opererer ofte på faste input-output-strukturer (f.eks. skjemaer, REST API-svar).
- AI Engineering: Mønstre må håndtere datavariabilitet både i struktur og skala, inkludert:
- Streaming av data for sanntidssystemer.
- Multimodale data (f.eks. tekst, bilder, videoer) som krever rørledninger med fleksible behandlingstrinn.
- Storskala datasett som trenger effektive forbehandlings- og utvidelsesrørledninger, ofte ved hjelp av mønstre som Builder eller Pipeline.
4. Eksperimentering vs. stabilitet
- Tradisjonell ingeniørfag: Det legges vekt på å bygge stabile, forutsigbare systemer der mønstre sikrer konsistent ytelse og pålitelighet.
- AI Engineering: AI arbeidsflyter er ofte eksperimentell og involverer:
- Iterering på forskjellige modellarkitekturer eller dataforbehandlingsteknikker.
- Dynamisk oppdatering av systemkomponenter (f.eks. omskolering av modeller, byttealgoritmer).
- Utvide eksisterende arbeidsflyter uten å bryte produksjonsrørledninger, ofte ved å bruke utvidbare mønstre som Decorator eller Factory.
Eksempel: En fabrikk i AI kan ikke bare instansiere en modell, men også legge til forhåndslastede vekter, konfigurere optimizere og koble treningstilbakekall – alt dynamisk.