97e91a83f3
Ruff / Ruff (push) Has been cancelled
Test / Core Tests (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.10) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.11) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.12) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.13) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.9) (push) Has been cancelled
Test / Full Coverage (Python 3.11) (push) Has been cancelled
Test / Core Provider Tests (OpenAI) (push) Has been cancelled
Test / Core Provider Tests (Anthropic) (push) Has been cancelled
Test / Core Provider Tests (Google) (push) Has been cancelled
Test / Core Provider Tests (Other) (push) Has been cancelled
Test / Anthropic Tests (push) Has been cancelled
Test / Gemini Tests (push) Has been cancelled
Test / Google GenAI Tests (push) Has been cancelled
Test / Vertex AI Tests (push) Has been cancelled
Test / OpenAI Tests (push) Has been cancelled
Test / Writer Tests (push) Has been cancelled
Test / Auto Client Tests (push) Has been cancelled
ty / type-check (push) Has been cancelled
193 lines
5.3 KiB
Markdown
193 lines
5.3 KiB
Markdown
---
|
|
authors:
|
|
- jxnl
|
|
categories:
|
|
- Pydantic
|
|
comments: true
|
|
date: 2024-03-08
|
|
description: Learn to generate synthetic data using Pydantic and OpenAI's models with
|
|
practical examples and configurations.
|
|
draft: false
|
|
tags:
|
|
- Synthetic Data
|
|
- Pydantic
|
|
- OpenAI
|
|
- Data Generation
|
|
- Python
|
|
---
|
|
|
|
# Simple Synthetic Data Generation
|
|
|
|
What that people have been using instructor for is to generate synthetic data rather than extracting data itself. We can even use the J-Schemo extra fields to give specific examples to control how we generate data.
|
|
|
|
Consider the example below. We'll likely generate very simple names.
|
|
|
|
```python
|
|
from typing import Iterable
|
|
from pydantic import BaseModel
|
|
import instructor
|
|
|
|
|
|
# Define the UserDetail model
|
|
class UserDetail(BaseModel):
|
|
name: str
|
|
age: int
|
|
|
|
|
|
# Patch the OpenAI client to enable the response_model functionality
|
|
client = instructor.from_provider("openai/gpt-5-nano")
|
|
|
|
|
|
def generate_fake_users(count: int) -> Iterable[UserDetail]:
|
|
return client.create(
|
|
model="gpt-5.4-mini",
|
|
response_model=Iterable[UserDetail],
|
|
messages=[
|
|
{"role": "user", "content": f"Generate a {count} synthetic users"},
|
|
],
|
|
)
|
|
|
|
|
|
for user in generate_fake_users(5):
|
|
print(user)
|
|
#> name='Alice' age=25
|
|
#> name='Bob' age=30
|
|
#> name='Charlie' age=22
|
|
#> name='David' age=28
|
|
#> name='Eve' age=35
|
|
```
|
|
|
|
## Leveraging Simple Examples
|
|
|
|
We might want to set examples as part of the prompt by leveraging Pydantics configuration. We can set examples directly in the JSON scheme itself.
|
|
|
|
```python
|
|
from typing import Iterable
|
|
from pydantic import BaseModel, Field
|
|
import instructor
|
|
|
|
|
|
# Define the UserDetail model
|
|
class UserDetail(BaseModel):
|
|
name: str = Field(examples=["Timothee Chalamet", "Zendaya"])
|
|
age: int
|
|
|
|
|
|
# Patch the OpenAI client to enable the response_model functionality
|
|
client = instructor.from_provider("openai/gpt-5-nano")
|
|
|
|
|
|
def generate_fake_users(count: int) -> Iterable[UserDetail]:
|
|
return client.create(
|
|
model="gpt-5.4-mini",
|
|
response_model=Iterable[UserDetail],
|
|
messages=[
|
|
{"role": "user", "content": f"Generate a {count} synthetic users"},
|
|
],
|
|
)
|
|
|
|
|
|
for user in generate_fake_users(5):
|
|
print(user)
|
|
#> name='John Doe' age=25
|
|
#> name='Alice Smith' age=30
|
|
#> name='Bob Johnson' age=28
|
|
#> name='Emily Brown' age=35
|
|
#> name='Michael Williams' age=27
|
|
```
|
|
|
|
By incorporating names of celebrities as examples, we have shifted towards generating synthetic data featuring well-known personalities, moving away from the simplistic, single-word names previously used.
|
|
|
|
## Leveraging Complex Example
|
|
|
|
To effectively generate synthetic examples with more nuance, lets upgrade to the "gpt-5.4-mini" model, use model level examples rather than attribute level examples:
|
|
|
|
```Python
|
|
import instructor
|
|
|
|
from typing import Iterable
|
|
from pydantic import BaseModel, ConfigDict
|
|
|
|
|
|
# Define the UserDetail model
|
|
class UserDetail(BaseModel):
|
|
"""Old Wizards"""
|
|
|
|
name: str
|
|
age: int
|
|
|
|
model_config = ConfigDict(
|
|
json_schema_extra={
|
|
"examples": [
|
|
{"name": "Gandalf the Grey", "age": 1000},
|
|
{"name": "Albus Dumbledore", "age": 150},
|
|
]
|
|
}
|
|
)
|
|
|
|
|
|
# Patch the OpenAI client to enable the response_model functionality
|
|
client = instructor.from_provider("openai/gpt-5-nano")
|
|
|
|
|
|
def generate_fake_users(count: int) -> Iterable[UserDetail]:
|
|
return client.create(
|
|
model="gpt-5.4-mini",
|
|
response_model=Iterable[UserDetail],
|
|
messages=[
|
|
{"role": "user", "content": f"Generate `{count}` synthetic examples"},
|
|
],
|
|
)
|
|
|
|
|
|
for user in generate_fake_users(5):
|
|
print(user)
|
|
#> name='Merlin' age=600
|
|
#> name='Radagast the Brown' age=950
|
|
#> name='Rincewind' age=70
|
|
#> name='Harry Potter' age=17
|
|
#> name='Elminster Aumar' age=1200
|
|
```
|
|
|
|
## Leveraging Descriptions
|
|
|
|
By adjusting the descriptions within our Pydantic models, we can subtly influence the nature of the synthetic data generated. This method allows for a more nuanced control over the output, ensuring that the generated data aligns more closely with our expectations or requirements.
|
|
|
|
For instance, specifying "Fancy French sounding names" as a description for the `name` field in our `UserDetail` model directs the generation process to produce names that fit this particular criterion, resulting in a dataset that is both diverse and tailored to specific linguistic characteristics.
|
|
|
|
|
|
```python
|
|
import instructor
|
|
|
|
from typing import Iterable
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
# Define the UserDetail model
|
|
class UserDetail(BaseModel):
|
|
name: str = Field(description="Fancy French sounding names")
|
|
age: int
|
|
|
|
|
|
# Patch the OpenAI client to enable the response_model functionality
|
|
client = instructor.from_provider("openai/gpt-5-nano")
|
|
|
|
|
|
def generate_fake_users(count: int) -> Iterable[UserDetail]:
|
|
return client.create(
|
|
model="gpt-5.4-mini",
|
|
response_model=Iterable[UserDetail],
|
|
messages=[
|
|
{"role": "user", "content": f"Generate `{count}` synthetic users"},
|
|
],
|
|
)
|
|
|
|
|
|
for user in generate_fake_users(5):
|
|
print(user)
|
|
#> name='Jean Luc' age=25
|
|
#> name='Marcelle' age=30
|
|
#> name='Antoinette' age=22
|
|
#> name='Gaspard' age=28
|
|
#> name='Eloise' age=35
|
|
``` |