| Pair | Local Qwen | OpenAI |
|---|---|---|
bank, banks |
0.9514 | 0.7883 |
bank, river |
0.6267 | 0.4143 |
bank, money |
0.8040 | 0.4347 |
river, money |
0.6139 | 0.3746 |
Graduate Quantitative Economics and Datascience
openai Python package: a client that speaks that API, installed with uv2b or 27b gives the approximate number of parameters in billions:cloud send prompts to a remote service and require an internet connectionStarting points for short text demos; download sizes are not total RAM usage.
| RAM | Computer | Model tag | Download |
|---|---|---|---|
| 8 GB | Any OS | qwen3.5:2b |
2.7 GB |
| 16 GB | Any OS | qwen3.5:4b |
3.4 GB |
| 32 GB | Any OS | qwen3.5:9b |
6.6 GB |
| 48 GB | Apple Silicon Mac | qwen3.8:27b-mlx |
18 GB |
| 64 GB | Apple Silicon Mac | qwen3.8:27b-mxfp8 |
32 GB |
| 32+ GB | Windows/Linux, 24+ GB VRAM | qwen3.8:27b |
18 GB |
ollama.dmg from ollama.com/download/mac and drag it to Applications, or, with Homebrew already installed,ollama serve in a separate terminal and leave it openollama pull qwen3.8:27b-mlx and ollama run qwen3.8:27b-mlxpull stores the weights in ~/.ollama; run loads them into memory and opens a prompt; ollama list shows what you haveOllamaSetup.exe from ollama.com/download/windows; the alternative winget install --id Ollama.Ollama uses a community-maintained package rather than an official onecurl -fsSL https://ollama.com/install.sh | sh installs a background service, then the same commands (a tarball for installs without root is on docs.ollama.com/linux)qwen3.5:4b on a Linux workstation; use ollama run qwen3.5:2b on an 8 GB laptopuv init local-llm, then uv add openai inside it), save the script below and uv run python local_llm.pyfrom openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
question = "Explain comparative advantage in two sentences."
response = client.chat.completions.create(
model="qwen3.5:2b",
messages=[
{"role": "system", "content": "You are a concise economics tutor."},
{"role": "user", "content": question},
],
)
print(response.choices[0].message.content)api_key, and Ollama ignores it, so any string worksopenai package is only a client: it turns the messages list into a JSON request, sends it over HTTP, and unpacks the JSON replylocalhost, so no request goes to OpenAI and nothing about your prompt leaves the machineollama pull qwen3-embedding:0.6b (639 MB)/v1/embeddings endpointimport numpy as np
from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
words = ["bank", "banks", "river", "money"]
response = client.embeddings.create(
model="qwen3-embedding:0.6b", input=words
)
E = np.array([item.embedding for item in response.data])
print(E.shape) # (4, 1024)The same words as the earlier embeddings example, rerun in September 2026 with local Qwen (1,024D) and hosted OpenAI (3,072D):
| Pair | Local Qwen | OpenAI |
|---|---|---|
bank, banks |
0.9514 | 0.7883 |
bank, river |
0.6267 | 0.4143 |
bank, money |
0.8040 | 0.4347 |
river, money |
0.6139 | 0.3746 |
bank closest to banks, then money, then riverlocal_embeddings.pyuv run python utilities/build_embedding_comparison.py, then render this deck with --no-cachemessages, roles, choices) became a de facto standard; Ollama, Google, and most hosting services serve the same shape as a compatibility interface| Backend | base_url |
api_key |
model |
|---|---|---|---|
| Ollama | http://localhost:11434/v1 |
any string | qwen3.5:2b |
| Google Gemini | https://generativelanguage.googleapis.com/v1beta/openai/ |
a Gemini key | gemini-3.8-flash |
| OpenAI | the default | an OpenAI key | gpt-4o-mini |
The NLP deck creates OpenAI() with the defaults (the last row); its chat and embedding code runs locally with the first row’s settings and a local embedding model name
Basic chat is portable; provider-specific features are not (Ollama’s endpoint has no logprobs or logit_bias, Gemini’s compatibility layer is beta, and embedding model names differ)
local_llm.pyimport os
from openai import OpenAI
client = OpenAI(
base_url=os.environ.get("LLM_BASE_URL", "http://localhost:11434/v1"),
api_key=os.environ.get("LLM_API_KEY", "ollama"),
)
question = "Explain comparative advantage in two sentences."
response = client.chat.completions.create(
model=os.environ.get("LLM_MODEL", "qwen3.5:2b"),
messages=[
{"role": "system", "content": "You are a concise economics tutor."},
{"role": "user", "content": question},
],
)
print(response.choices[0].message.content)LLM_BASE_URL=https://api.openai.com/v1, LLM_MODEL=gpt-4o-mini, and the key in LLM_API_KEY; the key never enters the file or gittemperature=0 and temperature=1.5; ask for JSON with response_format={"type": "json_object"}qwen3-embedding:0.6b and Ollama’s /api/embed documentationqwen3.5 and qwen3.8openai Python SDKmessages list of role dicts, a loop over articles, a comprehension over response.dataChatOpenAI bundles the client, model, and settingsSystemMessage, HumanMessage, and AIMessage replace the role dicts.batch(..., config={"max_concurrency": 3}) replaces the classifier loop and adds concurrency and retries.invoke works on ChatAnthropic or ChatGoogleGenerativeAI, whose wire formats are not OpenAI-compatiblelangchain_chat.py, after uv sync --extra extras in the course repositorymessages list is what every framework sends anyway