質性編碼的全文搜尋
找出提到某個詞的每一條訪談摘錄或開放式回答,並讓編碼者把匹配的條目拉入自己的佇列。
Potato 在伺服器啟動時為每個條目的文本建立全文索引,管理員可以在任何項目中搜索這個索引,無論項目是否啟用 QDA 模式。搜尋 copay 會返回每一條含有以 “copay” 開頭的詞的訪談摘錄或開放式問卷回答。結果按相關度排序,匹配的詞在一段簡短的摘要片段中高亮顯示。可選設定 search.annotator_claim 會在編碼頁面上新增一個 Find 面板。編碼者在 Find 面板中搜索同一個索引,並把匹配的條目認領到自己的佇列中。搜尋和 QDA 模式都是在 Potato 2.6.0 中加入的。
FTS5 是 SQLite 的全文搜尋擴充套件。SQLite 是 Potato 項目儲存背後的嵌入式資料庫。Potato 把索引作為一張表儲存在 <task_dir>/project.sqlite 中,備忘錄和碼本也儲存在同一個檔案裡。索引只包含條目中的詞。程式碼、備忘錄和標註都不在索引之內,搜尋也從不給搜尋結果中的段落應用程式碼。
在質性項目中,搜尋有兩種用途:
- 查詢要編碼的段落。 按主題通讀語料庫的編碼者搜尋 “bus” 或 “copay”,認領提到該詞的條目,再用跨度方案為相關段落編碼。
- 在整個語料庫中核查一個詞。 撰寫某個程式碼納入標準的分析者或裁定者搜尋一個詞,檢視哪些條目用到了這個詞。任何編碼者的佇列都不會改變。
配置
搜尋無需任何配置。沒有 search 塊的項目會在啟動時為每個條目建立索引,並提供管理員搜尋。下面的配置是一個完整的 QDA 模式項目,同時為編碼者開啟了搜尋並認領:
annotation_task_name: Clinic access interviews
task_dir: .
output_annotation_dir: annotation_output/
data_files:
- data/interviews.json
item_properties:
id_key: id
text_key: text
admin_api_key: change-this-key
qda_mode:
enabled: true
search:
annotator_claim: true
annotation_schemes:
- annotation_type: span
name: codes
description: Highlight a passage and apply a code
codebook: true
labels: [access barriers, cost concerns, provider trust]QDA 模式假定由一位編碼者處理整個語料庫。在 qda_mode.enabled: true 下,Potato 在任何分配設計中都接受 annotator_claim。有了 admin_api_key,你可以從指令碼或命令列呼叫管理員搜尋。
search 塊接受四個鍵:
| 選項 | 預設值 | 說明 |
|---|---|---|
search.enabled | true | 在啟動時建立索引並提供搜尋端點。設為 false 時,Potato 不建立索引,管理員搜尋返回 503。 |
search.backend | fts5 | 索引引擎。fts5 是 Potato 2.13.1 中唯一的後端。任何其他值都會關閉搜尋並在日誌中記錄一條警告。 |
search.max_instances | 100000 | 建立索引的條目數上限。Potato 按載入順序為條目建立索引,直到達到上限。超過上限的條目不進入索引,伺服器日誌會報告這個上限。 |
search.annotator_claim | false | 向編碼者顯示 Find 面板,並提供編碼者搜尋和認領端點。當該設定與分配設計衝突時,Potato 拒絕啟動(參見分配保護)。 |
誰可以搜尋
管理員搜尋和編碼者搜尋通過不同的端點讀取同一個索引:
| 誰 | 端點 | 何時可用 | 每次請求的結果數 |
|---|---|---|---|
| 管理員和裁定者 | GET /admin/api/search?q=<query>&limit=<n> | search.enabled 為 true | 預設 50,最多 500 |
| 任何已登入的編碼者 | GET /api/search?q=<query>&limit=<n>,以及 Find 面板 | search.annotator_claim: true | 預設 50,最多 200;Find 面板請求 50 |
| 任何已登入的編碼者 | POST /api/search/claim,請求體為 {"instance_id": "<id>"} | search.annotator_claim: true | 每次請求一個條目 |
管理員許可權指在 X-API-Key 請求頭中提供管理員 API 金鑰,或者以角色擁有管理後臺許可權的使用者身份登入。在啟用了 adjudication 塊的項目中,裁定者也可以使用管理員搜尋。管理員搜尋是隻讀的,不改變任何佇列。Potato 2.13.1 的管理後臺沒有搜尋框,管理員通過端點進行搜尋:
curl -H "X-API-Key: change-this-key" \
"http://localhost:8000/admin/api/search?q=copay&limit=20"端點返回如下結構的 JSON:
{
"count": 1,
"query": "copay",
"results": [
{
"id": "P01-03",
"instance_id": "P01-03",
"score": -0.525,
"snippet": "My \u0002copay\u0003 went up again this year, so I skipped the follow…"
}
]
}摘要片段最多包含條目中最匹配部分周圍的 12 個詞,並用控制字元 U+0002 和 U+0003 標出每個匹配的詞。Find 面板把這些標記轉換為高亮。score 是 FTS5 的 BM25 排名。分數越低表示匹配越好,結果按最佳匹配在前排列。count 是返回的結果數。limit 限制結果數,響應不會報告超出上限之外還有多少條目匹配。
查詢語法
Potato 在每個不是字母、數字或下劃線的字元處拆分查詢,並返回把拆分出的每個詞都作為字首包含在內的條目。Potato 從不把查詢作為查詢語法傳給 FTS5。下面的規則都來自這一步:
- 每個詞都必須出現。
bus line匹配同時包含 “bus” 和 “line” 的條目,兩個詞順序不限、間隔不限。 - 每個詞都按字首匹配。
copay匹配 “copay”、“copays” 和 “copayment”。care除了匹配 “care”,還匹配 “career” 和 “careful”。 - 忽略大小寫和重音符號。
Copay和copay返回相同的條目,cafe也能匹配 “café”。 - 標點會拆分單詞。
self-care搜尋的是 “self” 和 “care”。cost-of-care返回文本中任意位置含有 “cost”、“of” 和 “care” 的任何條目。 - 撇號會拆分單詞。
couldn't搜尋的是 “couldn” 和 “t”。含有 “couldn't” 的條目能被找到,同時摘要片段也會高亮其他以 “t” 開頭的詞。 - 運算子只是普通的詞。 引號和
*會被丟棄,OR、NOT和NEAR則變成條目必須包含的詞。像cost OR care這樣的查詢通常什麼也不返回。 - 不合並詞形變化。
run通過字首規則能匹配 “running”,而ran永遠匹配不到 “run”。請為參與者可能使用的每種詞形分別搜尋。
索引的文本與索引重建
Potato 為每個條目索引一個字串,按以下順序選取:
- 由
item_properties.text_key指定的欄位(預設text)。 text_key下的列表或物件,例如以發言輪次列表形式儲存的轉寫稿。Potato 索引編碼頁面所顯示的 JSON 文本,包括欄位名。- 對於
text_key下沒有可用值的條目,取caption、description、transcript、prompt、question、title、content和message中第一個非空欄位。
不含上述任何欄位的條目不進入索引,伺服器日誌會報告跳過了多少條目。沒有說明文字的影像是最常見的情況。條目 ID、participant_id 這樣的案例欄位以及其他任何條目欄位都不會被索引,因此搜尋參與者 ID 什麼也找不到。案例按參與者對摘錄分組。
Potato 每次啟動伺服器時都會重建整個索引。對於從 data_directory 載入的研究,Potato 會在目錄載入後再次重建索引,目錄監視器也會在有新檔案到達時重新建立索引。當 Python 構建所帶的 SQLite 缺少 FTS5 時,Potato 會關閉搜尋,搜尋端點返回 503,Potato 的其餘部分照常執行。
搜尋並認領
Find 面板
在 search.annotator_claim: true 下,編碼頁面會顯示一個 Find 按鈕。Find 面板接受一條查詢並列出最多 50 個匹配項,每項帶有摘要片段、條目 ID 和一個 Claim 按鈕。Claim 會把條目新增到編碼者佇列的末尾,並把按鈕替換為 “In your queue”。認領一個已在編碼者佇列中的條目不會改變任何東西,認領端點會報告 "already_assigned": true。面板不會開啟條目,編碼者要在佇列中向前推進才能到達已認領的條目。
認領端點接受項目中的任何條目 ID。另一位編碼者已持有的條目同樣可以被認領。未知 ID 返回 404,沒有會話的請求返回 401,未啟用 annotator_claim 的項目返回 403。
分配保護
認領條目的編碼者是在自行選擇樣本,而許多研究設計依賴於由 Potato 來做這個選擇。在 search.annotator_claim: true 下,如果配置還設定了以下任何一項,Potato 會拒絕啟動:
| 設定 | Potato 拒絕的原因 |
|---|---|
assignment_strategy 設為 random、diversity_clustering、max_diversity、active_learning、llm_confidence、least_annotated、category_based、batch、psychometric、model_review 或 engine | 該策略的樣本或順序依賴於由 Potato 選擇每個條目。 |
num_annotators_per_item 大於 1、overlap_sample.count 大於 1,或舊鍵 max_annotations_per_item 和 min_annotators_per_instance 大於 1 | Potato 無法保證重疊條目送達多位編碼者。 |
attention_checks.enabled 或 gold_standards.enabled | 編碼者可能找到並跳過檢查條目。 |
icl_labeling.enabled | 面向模型的盲驗證條目會變得可被搜到。 |
adjudication.enabled | 裁定佇列由 Potato 編排。 |
crowdsourcing.provider 設為 expert 以外的任何值,或存在舊版 prolific 或 mturk 塊或登入類型 | 付費任務是分配的單位,認領會破壞報酬結算和覆蓋範圍。 |
保護機制允許的分配策略是 fixed_order 和 priority,其中 fixed_order 是 Potato 的預設值。同時設定 assignment_strategy: random 和 num_annotators_per_item: 2 的配置無法通過校驗,並給出如下訊息:
ERRORS:
- search.annotator_claim: true is incompatible with this configuration:
- assignment_strategy: random (self-selection breaks sampling/ordering)
- num_annotators_per_item: 2 (inter-annotator overlap cannot be guaranteed under self-selection)在 qda_mode.enabled: true 或 solo_mode.enabled: true 下,Potato 會跳過上表中的所有檢查。因此,一個為編碼者間信度分配重疊樣本的 QDA 模式團隊項目會在開啟認領的狀態下啟動,編碼者隨後就能認領 Potato 分配給各自的樣本之外的條目。在團隊為信度樣本編碼期間請關閉 annotator_claim,之後如果讓編碼者自選條目符合研究設計,再開啟該設定。管理員搜尋不受保護機制約束,因為管理員搜尋不改變任何佇列。
在 QDA 模式中邊編碼邊搜尋
研究者若要在語料庫中搜索一個詞,再為該詞出現的每個段落編碼,在 Potato 中按以下步驟操作:
- 開啟 Find 並搜尋這個詞,例如
transport。字首規則也會返回 “transportation” 和 “transported”。 - 閱讀摘要片段,認領每一個摘要片段看起來相關的條目。
- 當已認領的條目在你的佇列中出現時,為其編碼。高亮段落並應用碼本中的程式碼,或按
i從選中文本生成一個原位程式碼。 - 為條目或選區附加一條備忘錄,記錄該段落為何符合這個程式碼,或某個近似段落為何落在程式碼之外。
搜尋返回的是條目,每個條目一段摘要片段。一個提到該詞五次的條目只出現一次,完整條目只能從佇列中開啟。Potato 2.13.1 沒有列出每次出現位置的上下文關鍵詞(keyword-in-context)列表,也沒有把程式碼一次性應用到所有搜尋匹配上的步驟。每一個編碼決定都由閱讀該段落的編碼者做出。
限制
- 只搜尋條目文本。 程式碼、備忘錄、標註、條目 ID 以及被索引文本以外的欄位都無法搜尋。
- 不支援短語、布林、鄰近或詞幹搜尋。 每個查詢都是一組必須全部出現的字首。
- 每個條目一段摘要片段。 摘要片段最多 12 個詞,無論該詞出現多少次,條目都只出現一次。
- 結果列表有上限。 管理員搜尋每次請求最多返回 500 個條目,編碼者搜尋最多 200 個,Find 面板 50 個。
- 不用空格分詞的語言。 中文或日文文本按標點之間的連續字元段建立索引,查詢只能在字元段的開頭匹配。
- 沒有文本的媒體。 影像、音訊或影片條目只能通過由
text_key指定或在後備欄位中找到的說明文字或轉寫稿欄位被索引。
上游示例項目
在 Potato 的原始碼目錄中,啟動上游的搜尋示例,並用示例的金鑰查詢管理員端點:
python potato/flask_server.py start examples/advanced/search-example/config.yaml -p 8000
curl -H "X-API-Key: search-example-key" \
"http://localhost:8000/admin/api/search?q=rare"該示例載入六份簡短的狀態報告,其中三份提到了 “rare”。示例在 fixed_order 分配下開啟搜尋並認領,並設定 max_annotations_per_user: 2。若要在一個訪談摘錄項目中檢視 Find 面板,請改為啟動位於 examples/advanced/qda-mode-example/config.yaml 的 QDA 模式示例。
延伸閱讀
有關實現細節,請參見源文件。