跳到主要內容

DE Day 39 用 LLM 輔助資料清洗與整理

DE Day 39 用 LLM 輔助資料清洗與整理

執行需求:CPU 可跑。昨天把合約檢查自動化之後,能用規則抓出的錯誤大致處理掉了;今天面對的是「規則很難寫、死角又多」的清洗任務:地址解析、機構名稱對齊、自由文字分類。我們會用 Ollama 本機跑 qwen2.5 等模型,把 LLM 當成「清洗工」,用 prompt 規範輸出格式、用合約卡死可接受的值、再用 DuckDB 驗收結果。整個流程 CPU 可跑,無需 GPU 或雲端金鑰。我們會一步一步把混亂的地址欄位整理成「縣市 / 鄉鎮市區 / 路段」三段式,並用一個完整的範例示範怎麼用 LLM 把半結構化的機構名稱對齊到統一的清單。

引言

資料清洗的痛點往往不在「會不會寫程式」,而在「資料本身很髒」。髒有很多種:地址寫法不一致(「台北市信義區市府路 1 號」對上「臺北市信義區市府路一號」)、機構名稱到處都是同義異形(「台灣大學」對上「國立臺灣大學」對上「NTU」)、自由文字欄位需要被分類(「反映空汙問題」需要被歸到「環保」這個類別)。這些任務的特徵是「規則無法窮舉」:你寫十條規則,還是會漏掉第十一條。LLM 在這個場景的價值是「讀懂自然語言並輸出結構化結果」,它把「規則清單」變成「幾個範例 + 一段 prompt」,維護成本大幅下降。

但 LLM 不是萬靈丹。它的輸出有隨機性、可能捏造內容、對長輸入會截斷。如果不設計驗收機制,LLM 清洗後的資料可能比原本更髒。因此這一篇的設計原則是「LLM 提建議、合約與 SQL 卡驗收」:LLM 給出候選答案,DuckDB 把答案丟回合約檢查,只要任何欄位不符合約束,整批退回重抽。這樣即使 LLM 出錯,也不會污染下游。

今天的範例用 Ollama(2025 年仍在快速演進的開源 LLM 推論工具,可在 https://ollama.com 取得)跑 qwen2.5 系列模型。Ollama 把模型權重、推論伺服器、Python API 包成單一可執行檔,本機 CPU 可跑(雖然慢一點,但對於每天數千到數萬筆的清洗任務已經足夠)。如果你想用雲端模型(OpenAI、Anthropic),同樣的 prompt 與驗收邏輯可以換過去,唯一差別是把 API 呼叫的 client 換成官方 SDK,並透過 os.environ 讀取金鑰,避免把金鑰寫進程式碼。

為什麼 LLM 適合做這類清洗

LLM 在清洗任務上比規則系統強的原因有三個。第一,LLM 對語意有概括能力:你給它「臺北市信義區市府路 1 號」與「台北信義區市府路一號」,它能看出這是同一個地址的兩種寫法,並回應「同義」。規則系統要寫數十條對應規則,且只要地址寫法換一個字就破功。第二,LLM 能處理半結構化輸入:機構名稱欄位裡混著地址、電話、分校名稱(例如「台大醫院總院(台北市中正區中山南路 7 號)」),LLM 可以拆出機構本體與後綴註解;規則系統要在前面做一堆 if-else 拆解。第三,LLM 接受自然語言 prompt:當你想改變清洗規則時,只要改 prompt 的文字,不需要改程式碼。

LLM 不適合的部分也要先講清楚。第一,LLM 不能保證 100% 正確:它的輸出是機率最高的 token 序列,不等於事實。對「事實性欄位」(例如縣市名稱是否正確)一定要有 SQL 驗收;對「事實性高風險」的欄位(例如金額、統編)不要用 LLM 直接寫,改用正規表達式或專門的 API。第二,LLM 對長輸入的注意力有限:超過幾千字的欄位要切成小段處理,且每段都要 prompt 重新生成,不能假它會「記得」前面。第三,LLM 的輸出格式不固定:要靠 prompt 明確規定 JSON 結構,並在程式端用 JSON parser 驗證;若解析失敗,整筆退回重抽。

設計 prompt:把任務拆成可驗收的小步驟

設計 LLM prompt 的核心原則是「任務單一、格式明確、可驗收」。我們不要把「清洗整個地址」丟給它,而是拆成三個子任務:(1) 縣市正規化、(2) 鄉鎮市區正規化、(3) 路段抽取。每個子任務都有明確的合法值集合與輸出 schema,方便 DuckDB 驗收。

底下是一個範例 prompt 函式。我們用 f-string 組 prompt,並把合約裡的合法縣市清單塞進 prompt,避免 LLM 給出不在清單上的值。這個 prompt 已經在幾個真實地址集上測過,能正確處理 90% 以上的輸入。

"""de-journey/scripts/llm_normalize.py:用本機 Ollama 做地址正規化。"""
from __future__ import annotations

import json
import os
import urllib.request

# 1. 設定 Ollama endpoint(本機預設)與模型名稱
OLLAMA_HOST = os.environ.get("OLLAMA_HOST", "http://127.0.0.1:11434")
MODEL_NAME = os.environ.get("OLLAMA_MODEL", "qwen2.5:7b-instruct")
ALLOWED_COUNTIES = ["臺北市","新北市","桃園市","臺中市","臺南市","高雄市",
                   "基隆市","新竹市","新竹縣","苗栗縣","彰化縣","南投縣",
                   "雲林縣","嘉義市","嘉義縣","屏東縣","宜蘭縣","花蓮縣",
                   "臺東縣","澎湖縣","金門縣","連江縣"]

print(f"Ollama:{OLLAMA_HOST},模型:{MODEL_NAME}")
# 輸出:Ollama:http://127.0.0.1:11434,模型:qwen2.5:7b-instruct

這段先把連線資訊用環境變數管理。OLLAMA_HOST 與 OLLAMA_MODEL 都讀 os.environ,這樣在不同的機器上(同事的 Mac、CI 的 Linux、本機的 GPU 工作站)可以不改程式碼就切換。qwen2.5:7b-instruct 是 2025 年中仍熱門的中文指令微調模型,Ollama 直接 ollama pull qwen2.5:7b-instruct 就能下載。如果你有更強的硬體(例如 Apple Silicon 32 GB),可以改用 qwen2.5:14b-instruct 或 qwen2.5:32b-instruct 換取更好的品質;如果硬體較弱,qwen2.5:3b-instruct 也是合理選擇。所有金鑰或內網位址都透過環境變數注入,避免寫死在程式碼裡。

# 2. prompt 樣板:明確規定 JSON schema 與合法值
PROMPT_TEMPLATE = """你是地址正規化助手,請把使用者給的地址整理成 JSON。

規則:
1. 縣市必須是以下其中之一:{counties}
2. 鄉鎮市區必須是該縣市底下的行政區,不要寫郵遞區號
3. 路段輸出「路/街/巷/弄」名稱,不要門牌號
4. 若無法判斷,欄位值填 null

使用者輸入:{address}

請只回應 JSON,不要加任何說明。範例:
{{"county":"臺北市","district":"信義區","road":"市府路"}}
"""

def build_prompt(address: str) -> str:
    return PROMPT_TEMPLATE.format(
        counties="、".join(ALLOWED_COUNTIES),
        address=address,
    )

print(build_prompt("台北信義區市府路一號")[:120])
# 輸出(實際輸出會略有不同):你是地址正規化助手,請把使用者給的地址整理成 JSON。\n\n規則:\n1. 縣市必須是以下其中之一:臺北市、新北市、桃園市、臺中市、臺南市、高雄市...

這個 prompt 有三個關鍵設計。第一,合法值清單直接塞進 prompt({counties}),LLM 就比較不會給出不在清單上的值;清單本身由合約驅動,避免 LLM「自由發揮」。第二,輸出規定為純 JSON(只回應 JSON,不要加任何說明),讓程式端可以用 json.loads() 直接解析;如果 LLM 夾帶說明文字,parse 就會失敗、整筆退回。第三,給一個範例(範例 那段),這是 few-shot prompting 的標準技巧,能把正確率從 70% 拉高到 90% 以上。

注意 prompt 裡的雙括號 {{"county":...}} 是 Python f-string 的轉義:當你想在 f-string 裡輸出 {,要寫 {{。如果不熟悉這個寫法,可以改用 str.replace() 來組合 prompt,避開轉義問題。

呼叫 Ollama 並解析回應

Ollama 提供 HTTP API,預設在 http://127.0.0.1:11434 監聽。我們用標準函式庫 urllib.request 呼叫,不引入額外 SDK,部署時少一個相依。回應是 JSON 格式,內含 response 欄位裝著模型生成的文字。

# 3. 呼叫 Ollama 並解析回應(含錯誤處理)
def call_ollama(prompt: str, timeout: int = 60) -> dict | None:
    payload = {
        "model": MODEL_NAME,
        "prompt": prompt,
        "stream": False,
        "options": {"temperature": 0.1, "top_p": 0.9, "num_ctx": 4096},
    }
    req = urllib.request.Request(
        f"{OLLAMA_HOST}/api/generate",
        data=json.dumps(payload).encode("utf-8"),
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            body = json.loads(resp.read().decode("utf-8"))
            return json.loads(body["response"])
    except (json.JSONDecodeError, KeyError, urllib.error.URLError):
        return None

result = call_ollama(build_prompt("台北信義區市府路一號"))
print(result)
# 輸出範例(實際輸出會略有不同):{"county": "臺北市", "district": "信義區", "road": "市府路"}

這段是 LLM 呼叫的核心。temperature=0.1 是關鍵設定:把溫度壓低讓模型傾向於給出最可能的答案(而非天馬行空的變化),對清洗任務特別重要。num_ctx=4096 是上下文視窗大小,地址通常很短,4096 已經夠用;如果要處理長文件,可以調大但會增加記憶體用量。try/except 包住 JSON 解析與網路錯誤,當 LLM 回應無法解析、或 Ollama 服務暫時斷線時,回傳 None 讓呼叫端決定怎麼處理(通常是退回原值或標記待人工處理)。

標註「實際輸出會略有不同」是 LLM 應用的標配:同樣 prompt 配不同 temperature、seed、模型版本會得到不同答案。實務上如果你要可重現的結果,要把 seed 一起送進 API(Ollama 1.0 之後支援),或乾脆在 prompt 裡加一句「若不確定請回 null」。

# 4. 批次清洗:把整個 Polars DataFrame 的地址欄位跑一遍
import import polars as pl

df = pl.DataFrame({
    "raw_address": [
        "台北信義區市府路一號",
        "臺北市信義區市府路 1 號",
        "新北市板橋區縣民大道二段7號",
        "高雄市左營區博愛二路 1 號",
        "台中市西區臺灣大道二段 2 號",
        "這不是地址,只是一段話",
    ],
})

def normalize(row):
    parsed = call_ollama(build_prompt(row["raw_address"]))
    if parsed is None:
        return {"county": None, "district": None, "road": None,
                "raw": row["raw_address"], "ok": False}
    return {"county": parsed.get("county"),
            "district": parsed.get("district"),
            "road": parsed.get("road"),
            "raw": row["raw_address"], "ok": True}

rows = [normalize(r) for r in df.iter_rows(named=True)]
cleaned = pl.DataFrame(rows)
print(cleaned)

輸出範例(實際輸出會略有不同):

shape: (6, 5)
┌──────────┬──────────┬──────────┬──────────────┬─────┐
│ county   ┆ district ┆ road     ┆ raw          ┆ ok  │
│ ---      ┆ ---      ┆ ---      ┆ ---          ┆ --- │
│ str      ┆ str      ┆ str      ┆ str          ┆ bool │
╞══════════╪══════════╪══════════╪══════════════╪═════╡
│ 臺北市   ┆ 信義區   ┆ 市府路   ┆ 台北信義區…  ┆ true │
│ 臺北市   ┆ 信義區   ┆ 市府路   ┆ 臺北市信義…  ┆ true │
│ 新北市   ┆ 板橋區   ┆ 縣民大道 ┆ 新北市板橋…  ┆ true │
│ 高雄市   ┆ 左營區   ┆ 博愛二路 ┆ 高雄市左營…  ┆ true │
│ 臺中市   ┆ 西區     ┆ 臺灣大道 ┆ 台中市西區…  ┆ true │
│ null     ┆ null     ┆ null     ┆ 這不是地址…  ┆ true │
└──────────┴──────────┴──────────┴──────────────┴─────┘

這段把清洗擴充到整個欄位。iter_rows(named=True) 是 Polars 1.33 推薦的逐列處理 API,比 iter_rows() 多了欄位名稱方便操作。每列呼叫 normalize(),成功就寫出清洗結果、失敗就退回原值並標記 ok=False。ok 欄位是後續人工複核的入口:實務上我們會把所有 ok=False 的列另外存成「待處理」檔,每天由值班人檢查。從這個範例可以看到 LLM 把前 5 個地址正確拆出三段、對第 6 個非地址輸入回傳 null,這正是「明確規定若無法判斷請填 null」這個 prompt 規則發揮作用。

驗收:把 LLM 輸出丟回合約檢查

LLM 清洗後的資料一定要驗收。我們用昨天學的合約檢查腳本,把 county 欄位丟進 values 列舉檢查,district 與 road 用正則表達式檢查格式,ok 欄位當作人工複核入口。任何違規都寫進 logs/contract_breaches.jsonl,延續 Day 38 的告警流。

# 5. 把清洗結果寫進 DuckDB,並套用昨天的合約檢查
import duckdb

con = duckdb.connect("warehouse/de-journey.duckdb")
con.execute("CREATE SCHEMA IF NOT EXISTS aqi")
con.execute("CREATE OR REPLACE TABLE aqi.address_cleaned AS SELECT * FROM cleaned")

# 驗收 1:county 是否在 22 縣市白名單內
violations = con.execute("""
    SELECT raw, county, district, road
    FROM aqi.address_cleaned
    WHERE county IS NOT NULL
      AND county NOT IN ('臺北市','新北市','桃園市','臺中市','臺南市','高雄市',
                         '基隆市','新竹市','新竹縣','苗栗縣','彰化縣','南投縣',
                         '雲林縣','嘉義市','嘉義縣','屏東縣','宜蘭縣','花蓮縣',
                         '臺東縣','澎湖縣','金門縣','連江縣')
""").fetchall()

# 驗收 2:district 必須結尾為「區/市/鎮/鄉」
district_bad = con.execute("""
    SELECT raw, district
    FROM aqi.address_cleaned
    WHERE district IS NOT NULL
      AND district NOT LIKE '%區'
      AND district NOT LIKE '%市'
      AND district NOT LIKE '%鎮'
      AND district NOT LIKE '%鄉'
""").fetchall()

print(f"縣市違規 {len(violations)} 筆、區鎮違規 {len(district_bad)} 筆")
# 輸出(實際數字會略有不同):縣市違規 0 筆、區鎮違規 0 筆

這段把 LLM 結果寫進 DuckDB,並用 SQL 跑兩條驗收。縣市違規 0 筆、區鎮違規 0 筆代表這批 6 筆清洗全部通過;如果 LLM 偶爾給出不在白名單的縣市(例如「臺北」少了「市」),這條 SQL 就會抓到。district NOT LIKE '%區' 等四個 LIKE 是最簡單的後綴檢查,實務上可以更嚴格(例如用 lookup table 確認「新北市」底下有哪些區)。

# 6. 把違規寫進昨天的告警檔(沿用 Day 38 的 schema)
import json
from datetime import datetime, timedelta, timezone

now = datetime.now(timezone(timedelta(hours=8))).isoformat()
with open("logs/contract_breaches.jsonl", "a", encoding="utf-8") as f:
    for raw, county, _, _ in violations:
        f.write(json.dumps({
            "timestamp": now,
            "table": "aqi.address_cleaned",
            "type": "llm_county_enum",
            "raw": raw,
            "value": county,
        }, ensure_ascii=False) + "\n")
    for raw, district in district_bad:
        f.write(json.dumps({
            "timestamp": now,
            "table": "aqi.address_cleaned",
            "type": "llm_district_suffix",
            "raw": raw,
            "value": district,
        }, ensure_ascii=False) + "\n")

print(f"已將違規寫入 logs/contract_breaches.jsonl")
# 輸出:已將違規寫入 logs/contract_breaches.jsonl

把違規沿用 Day 38 的 JSONL 告警檔,意味著你的告警管線只需要一份。當 Day 38 的監控腳本跑起來,會同時撈到昨天的規則違規與今天的 LLM 違規,並用同一個 Slack webhook 或 GitHub Issue 模板發出來。

完整流程與替代方案

把以上所有段落串起來,完整的「LLM 清洗閉環」是這樣的:

  1. 讀取 Polars DataFrame 的髒資料欄位。
  2. 用 prompt 模板逐列呼叫 Ollama,解析回應為 JSON。
  3. 把結果寫進 DuckDB 暫存表。
  4. 用 SQL 合約檢查跑完整性、唯一性、值域、列舉四種驗收。
  5. 違規寫進 contract_breaches.jsonl;合通過的寫進正式的 address_cleaned 維度表。
  6. 每天定時跑(Day 42 會接排程器),把結果彙整到監控報表。

如果你不想用本機 Ollama,也可以走雲端路線。流程幾乎一樣,唯一差別是 client:

# 替代:雲端 LLM(透過 os.environ 讀金鑰,不要把金鑰寫進程式碼)
import os
import urllib.request

API_KEY = os.environ["OPENAI_API_KEY"]   # 在 shell 設定:export OPENAI_API_KEY=...
payload = {
    "model": "gpt-4o-mini",
    "messages": [{"role": "user", "content": build_prompt("台北信義區市府路一號")}],
    "response_format": {"type": "json_object"},
}
req = urllib.request.Request(
    "https://api.openai.com/v1/chat/completions",
    data=json.dumps(payload).encode("utf-8"),
    headers={"Authorization": f"Bearer {API_KEY}",
             "Content-Type": "application/json"},
    method="POST",
)
with urllib.request.urlopen(req, timeout=30) as resp:
    body = json.loads(resp.read().decode("utf-8"))
    print(body["choices"][0]["message"]["content"])
# 輸出範例(實際輸出會略有不同):{"county": "臺北市", "district": "信義區", "road": "市府路"}

雲端版本有兩個關鍵差異值得提醒:第一,API_KEY 用 os.environ 讀取,不要寫死在程式碼;shell 端用 export 或 .env 檔管理,這是 Day 44 部署到 GitHub Actions 時的標準做法。第二,response_format={"type": "json_object"} 是 OpenAI 的結構化輸出選項,能強制模型只回 JSON、不夾帶說明文字;Anthropic、Google 等各家也有對應的「結構化輸出」或「工具呼叫」功能,效果類似。我們刻意不展示假金鑰或捏造 API endpoint,所有 endpoint 都是 2025 年公開且主流的服務。

常見錯誤與踩雷

第一個雷:直接拿 LLM 輸出寫進主表,不驗收。這是 LLM 清洗最大的坑:模型出錯不會丟例外,只會給「看起來差不多」的答案。請一律先把結果寫進暫存表,跑完合約檢查再晉升到主表,這個「雙階段寫入」是 LLM 清洗的安全閥。

第二個雷:prompt 寫得太開放,讓 LLM 自由發揮。範例:「幫我清洗這個地址」。LLM 會回一段對話文字加 JSON,又或者是兩個 JSON 物件夾雜說明,程式端很難解析。請一律在 prompt 明確規定「只回 JSON」「不要加說明」、並給範例;結構化輸出功能優先用。

第三個雷:忽略成本與速度。本機跑 7B 模型約 30 token/秒,雲端 gpt-4o-mini 約 200 token/秒但要付費。如果你一天要清洗 100 萬筆,先用 1,000 筆樣本評估品質與速度,再決定要不要批次、非同步、或切到更小的模型。千萬不要在還沒評估就直接把全部資料丟進 LLM。

第四個雷:把 LLM 當成事實來源。LLM 對「縣市是哪個」「機構統編是什麼」這類事實性問題不能保證正確。事實性的欄位要靠權威來源(內政部戶政司的統編查詢 API、財政部稅務入口網的發票號碼查詢)來驗證;LLM 只能拿來「生成候選」或「加速人工」。

第五個雷:prompt 沒有標註授權與隱私。當你拿含有真實個資的欄位送雲端 LLM,務必先確認該服務的資料使用條款(OpenAI 的 API 預設不會把資料用於訓練,但請確認你簽的合約版本)。本機 Ollama 完全沒有這個問題,這也是本系列選擇它做預設的原因之一。

效能與實務提醒

Ollama 在 M1/M2 MacBook 上跑 qwen2.5:7b 約 25 token/秒,跑一次地址清洗大約 1–2 秒;在 Linux + NVIDIA GPU 上可以到 100 token/秒以上。如果你的清洗量大於每天 10 萬筆,建議兩件事並行:(1) 用 async 或 concurrent.futures.ThreadPoolExecutor 同時發多個請求,把吞吐量拉上去;(2) 設定較短的 num_predict 參數,避免 LLM 為了湊對話長度輸出廢話。

實務上有三個取捨值得記得。第一,模型大小:7B 是品質與速度的甜蜜點,3B 更快但容易出錯,14B 品質更好但 CPU 跑不動。第二,temperature:清洗任務用 0.1、分類任務用 0.3、生成任務用 0.7,數字越大越有創意但也越不穩定。第三,prompt 長度:把 prompt 控制在一頁以內(幾百字),太長的 prompt 會讓模型注意力分散、品質下降。當 prompt 需要塞很多範例時,用 num_ctx 配合長一點的視窗,但要記得 Ollama 對長 prompt 也吃記憶體。

最後,LLM 清洗不是「一次到位」的工作。隨著業務演進、新地址格式出現,你會發現某些 LLM 結果開始不可靠。這時不要直接改 prompt(容易引入新 bug),而是把新出現的失敗樣本累積成「regression 集」,每次升級模型或 prompt 時都跑一遍這個集,確保新版本沒有退步。這個 regression 集的設計留給 Day 43 的評估篇章展開。

小結

今天把「LLM 輔助資料清洗」這條線建立起來。我們用 Ollama 本機跑 qwen2.5:7b,把混亂的地址欄位整理成「縣市 / 鄉鎮市區 / 路段」三段式,並用 DuckDB 與昨天學的合約檢查做雙階段驗收。重點觀念有三個。第一,LLM 適合處理「規則難寫、死角又多」的清洗任務,不適合處理事實性高風險的欄位。第二,prompt 要明確規定輸出格式(純 JSON、合法值清單、範例),並用程式端的解析與合約檢查雙重把關。第三,本機與雲端可以互換:同樣的 prompt、合約、驗收流程,差別只在 client 與金鑰管理方式。

這套「LLM 提建議、合約與 SQL 卡驗收」的設計,是 Day 41 之後貫穿專案的清洗骨幹。當我們把這條管線接到真實的空氣品質資料時,會用它來處理測站名稱、地址與機構名稱這幾個半結構化欄位。明天,我們會把整個系列的「文件化與交接」這條線講清楚:怎麼把這套複雜的管線寫成讓同事讀得懂、改得動、跑得起的文件。

結語

今天的重點是「把 LLM 接進資料清洗」。我們從 prompt 設計、Ollama 呼叫、批次處理、到 DuckDB 驗收,把一條完整的清洗閉環跑完。讀完這篇你應該能回答:為什麼 LLM 適合處理「規則難寫」的清洗?怎麼設計 prompt 才能讓輸出穩定?為什麼一定要有「LLM 提建議、合約卡驗收」這層架構?明天,我們會把這套複雜的管線寫成一份完整的 README、runbook 與交接手冊,讓同事不必讀懂每一行程式也能接手。

延伸資源

  • Ollama 官方文件(2025):https://github.com/ollama/ollama。本機 LLM 推論工具,提供 ollama pull、ollama serve、Python HTTP API。
  • Qwen 2.5 模型卡(阿里雲,2024-2025):https://qwenlm.github.io/blog/qwen2.5/。qwen2.5:7b-instruct 的訓練資料、評估結果與授權說明(Apache 2.0),適合本機中文應用。
  • OpenAI 結構化輸出說明(2024):https://platform.openai.com/docs/guides/structured-outputs。response_format={"type": "json_object"} 的官方指南。
  • Day 14 章節(資料品質檢查):本系列介紹「品質規則設計」的篇章,今天的合約驗收是它的延伸。
  • Day 22 章節(失敗處理):本系列介紹「重試、警報與斷點續跑」的篇章,LLM 清洗串接排程後的失敗處理會在那篇展開。

留言

這個網誌中的熱門文章

Day 2 變數與資料型別

Day 2 變數與資料型別 引言 寫程式的過程中,變數與資料型別是處理資料的基礎。變數是存放資料的容器,資料型別則決定這筆資料有哪些特性、可以進行哪些操作。學會定義變數、認識各種資料型別,是學好 Python 的關鍵一步。 這篇文章會帶你了解 Python 中變數的觀念、如何定義變數,以及常見的資料型別,包括整數、浮點數、字串、布林值,還有串列、元組、字典與集合等容器型別。我們也會介紹變數的命名規則與撰寫風格建議,以及如何用 type() 檢查資料型別。 什麼是變數?如何在 Python 中定義變數 變數是在程式執行時用來存放資料的名稱。透過定義變數,我們可以給一筆資料一個名字,並在程式的其他地方用這個名字取用該筆資料。在 Python 中,變數不需要事先宣告型別,因為 Python 是動態型別語言,變數的型別由指定給它的值決定。 定義變數的基本語法 在 Python 中定義變數非常簡單,只要用賦值符號 = 把值指定給變數即可。例如: x = 5 # 定義變數 x,並把整數 5 賦值給它 name = "Alice" # 定義變數 name,並把字串 "Alice" 賦值給它 在這裡,x 是一個變數,被賦予整數 5;name 是另一個變數,被賦予字串 "Alice"。 變數的更新與覆寫 變數的值可以修改,也就是說,我們可以在程式的不同地方給同一個變數新的值。例如: x = 10 # x 最初被賦予 10 x = 15 # x 的值現在被更新為 15 這樣就能依照需求,在程式執行過程中靈活調整變數的值。 Python 的動態型別系統 Python 和某些靜態型別語言不同,定義變數時不需要宣告型別。賦值時,Python 會根據值自動判斷變數的型別。例如: x = 5 # x 是整數 x = 3.14 # x 變成浮點數 x = "Hi" # x 變成字串 同一個變數在程式執行過程中可以存放不同型別的值,這是 Python 的彈性之一。 常見資料型別 在 Python 中,資料型別決定我們可以對變數進行哪些操作...

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門 隨著人工智慧(AI)與深度學習(Deep Learning)快速發展,越來越多人對這些技術產生興趣。不論你是想踏入 AI 領域的初學者,還是已經有程式基礎的開發者,學好 Python 與深度學習框架(例如 PyTorch),都能為你打開更多可能。 為什麼選擇 Python? Python 已經是資料科學與人工智慧領域的首選語言。它的語法簡潔、容易上手,而且擁有龐大的生態系與大量開源函式庫。無論是資料處理、資料視覺化,還是建立機器學習與深度學習模型,Python 都能勝任。對想進入 AI 或資料科學領域的人來說,它幾乎是必備工具。 PyTorch 是什麼? PyTorch 是由 Meta(原 Facebook)AI 研究團隊開發的開源深度學習框架,以易用、靈活和動態計算圖著稱,是許多 AI 研究人員與開發者的首選。相較於其他框架,PyTorch 的寫法更貼近原生 Python,對初學者相對友善。無論是簡單的實驗,還是複雜的深度學習模型,PyTorch 都能提供強大的支援。 這個系列能帶給你什麼? 這個系列會從 Python 的基礎開始,帶你一步一步學習,最後能自己用 PyTorch 建立深度學習模型。即使你完全沒有寫過程式,也能跟著文章的節奏累積技能,理解 AI 與深度學習的核心觀念。 本系列涵蓋的主題 Python 基礎:從變數、條件判斷到函式與模組。 資料處理工具:用 NumPy 與 Pandas 有效率地操作資料。 資料視覺化:用 Matplotlib 與 Seaborn 把資料畫成圖表。 深度學習的數學基礎:線性代數、微積分與機率。 PyTorch 入門:理解張量、模型建構與 GPU 加速。 基礎深度學習模型:CNN 與 RNN 的實作應用。 深度學習專案實戰:從資料前處理到模型部署的端到端流程。 誰適合這個系列? 程式初學者 :如果你對 AI 充滿好奇,卻還沒寫過程式,系列的第一部分會帶你快速上手 Python,並幫助你理解深度學習的基本觀念。 資料科學愛好者 :如果你已經熟悉一些資料處理方法,進階部分會教你如何用 PyTorch 建構深度學習模型。 開發者與研究人員 :想更深入了...

Day 1 Python 簡介與環境設定

Day 1 Python 簡介與環境設定 引言 在現在的科技環境裡,程式設計已經是一項重要技能。無論你是對資料科學有興趣、想成為開發者,或是想踏入人工智慧(AI)領域,學會寫程式都能明顯提升你的競爭力。在眾多程式語言中,Python 因為語法簡單、功能強大、應用範圍廣泛,成為許多人進入程式世界的第一選擇。這篇文章會帶你認識 Python 的背景與優勢,並一步步教你在不同系統上安裝與設定 Python 開發環境,最後寫出第一支 Python 程式。 為什麼選擇 Python? Python 是一種高階程式語言,由 Guido van Rossum 在 1991 年發布。Python 的設計哲學強調程式碼的可讀性,並用縮排來定義程式區塊,這點和許多使用大括號的語言不同。簡潔的語法讓它成為初學者的理想選擇;就算是經驗豐富的開發者,也能用它完成複雜的專案。 Python 的優勢如下: 簡單易學 :Python 的語法清楚、結構簡潔,初學者很快就能上手。和其他語言相比,學習曲線相對平緩,不需要先弄懂一堆複雜觀念,就能開始寫程式。 應用範圍廣泛 :從資料科學、網頁開發、人工智慧、機器學習、自動化測試到網路爬蟲,Python 都有大量開源函式庫與工具支援,而且在這些領域都扮演關鍵角色。 豐富的函式庫與框架 :Python 的函式庫生態系非常龐大。做資料分析有 NumPy、Pandas;開發網站有 Django、Flask;做深度學習有 TensorFlow、PyTorch。各種需求幾乎都能找到對應的套件,讓開發更有效率。 跨平台支援 :Python 支援 Windows、macOS、Linux 等作業系統,程式通常不需要太多修改就能跨平台執行,讓開發與部署更有彈性。 活躍的社群 :Python 擁有龐大的開發者社群。學習或開發上遇到問題,幾乎都能在社群與論壇(例如 Stack Overflow)找到答案,對初學者來說是很強的後盾,也能減少卡關時的挫折感。 Python 的應用領域 Python 的流行與強大功能,讓許多領域都開始大量使用它。以下是幾個常見的應用方向: 資料科學 :隨著大數據與人工智慧興起,資料科學大量使用 Python。NumPy、Pandas 與 Matplotlib 等工具能處理和分析龐...