跳轉到內容

質性編碼的全文搜尋

找出提到某個詞的每一條訪談摘錄或開放式回答,並讓編碼者把匹配的條目拉入自己的佇列。

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 模式項目,同時為編碼者開啟了搜尋並認領:

yaml
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.enabledtrue在啟動時建立索引並提供搜尋端點。設為 false 時,Potato 不建立索引,管理員搜尋返回 503。
search.backendfts5索引引擎。fts5 是 Potato 2.13.1 中唯一的後端。任何其他值都會關閉搜尋並在日誌中記錄一條警告。
search.max_instances100000建立索引的條目數上限。Potato 按載入順序為條目建立索引,直到達到上限。超過上限的條目不進入索引,伺服器日誌會報告這個上限。
search.annotator_claimfalse向編碼者顯示 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 的管理後臺沒有搜尋框,管理員通過端點進行搜尋:

bash
curl -H "X-API-Key: change-this-key" \
  "http://localhost:8000/admin/api/search?q=copay&limit=20"

端點返回如下結構的 JSON:

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 為每個條目索引一個字串,按以下順序選取:

  1. 由 item_properties.text_key 指定的欄位(預設 text)。
  2. text_key 下的列表或物件,例如以發言輪次列表形式儲存的轉寫稿。Potato 索引編碼頁面所顯示的 JSON 文本,包括欄位名。
  3. 對於 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 大於 1Potato 無法保證重疊條目送達多位編碼者。
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 的配置無法通過校驗,並給出如下訊息:

text
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 中按以下步驟操作:

  1. 開啟 Find 並搜尋這個詞,例如 transport。字首規則也會返回 “transportation” 和 “transported”。
  2. 閱讀摘要片段,認領每一個摘要片段看起來相關的條目。
  3. 當已認領的條目在你的佇列中出現時,為其編碼。高亮段落並應用碼本中的程式碼,或按 i 從選中文本生成一個原位程式碼。
  4. 為條目或選區附加一條備忘錄,記錄該段落為何符合這個程式碼,或某個近似段落為何落在程式碼之外。

搜尋返回的是條目,每個條目一段摘要片段。一個提到該詞五次的條目只出現一次,完整條目只能從佇列中開啟。Potato 2.13.1 沒有列出每次出現位置的上下文關鍵詞(keyword-in-context)列表,也沒有把程式碼一次性應用到所有搜尋匹配上的步驟。每一個編碼決定都由閱讀該段落的編碼者做出。

限制

  • 只搜尋條目文本。 程式碼、備忘錄、標註、條目 ID 以及被索引文本以外的欄位都無法搜尋。
  • 不支援短語、布林、鄰近或詞幹搜尋。 每個查詢都是一組必須全部出現的字首。
  • 每個條目一段摘要片段。 摘要片段最多 12 個詞,無論該詞出現多少次,條目都只出現一次。
  • 結果列表有上限。 管理員搜尋每次請求最多返回 500 個條目,編碼者搜尋最多 200 個,Find 面板 50 個。
  • 不用空格分詞的語言。 中文或日文文本按標點之間的連續字元段建立索引,查詢只能在字元段的開頭匹配。
  • 沒有文本的媒體。 影像、音訊或影片條目只能通過由 text_key 指定或在後備欄位中找到的說明文字或轉寫稿欄位被索引。

上游示例項目

在 Potato 的原始碼目錄中,啟動上游的搜尋示例,並用示例的金鑰查詢管理員端點:

bash
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 模式示例。

延伸閱讀

有關實現細節,請參見源文件。