NLP 課程文件

建立您自己的資料集

Hugging Face's logo
加入 Hugging Face 社群

並獲得增強型文件體驗

開始

建立您自己的資料集

Ask a Question Open In Colab Open In Studio Lab

有時,構建 NLP 應用程式所需的資料集並不存在,因此您需要自己建立它。在本節中,我們將向您展示如何建立 GitHub 問題 語料庫,這些問題通常用於跟蹤 GitHub 儲存庫中的錯誤或功能。此語料庫可用於各種目的,包括

  • 探索關閉開放問題或拉取請求所需的時間
  • 訓練一個可以根據問題描述(例如,“錯誤”、“增強”或“問題”)對問題進行元資料標記的 *多標籤分類器*
  • 建立語義搜尋引擎以查詢與使用者查詢匹配的問題

這裡我們將重點介紹建立語料庫,在下一節中,我們將解決語義搜尋應用程式。為了保持一致性,我們將使用與流行的開源專案相關的 GitHub 問題:🤗 資料集!讓我們看看如何獲取資料並探索這些問題中包含的資訊。

獲取資料

您可以透過導航到儲存庫的 問題標籤 來找到 🤗 資料集中的所有問題。如以下螢幕截圖所示,在撰寫本文時,有 331 個開放問題和 668 個已關閉問題。

The GitHub issues associated with 🤗 Datasets.

如果您點選其中一個問題,您會發現它包含標題、描述和一組表徵問題的標籤。以下螢幕截圖顯示了一個示例。

A typical GitHub issue in the 🤗 Datasets repository.

要下載所有儲存庫的問題,我們將使用 GitHub REST API 輪詢 問題 端點。此端點返回一個 JSON 物件列表,每個物件包含大量欄位,包括標題和描述,以及有關問題狀態等的元資料。

下載問題的便捷方法是透過 requests 庫,這是在 Python 中進行 HTTP 請求的標準方法。您可以透過執行以下命令安裝該庫

!pip install requests

安裝庫後,您可以透過呼叫 requests.get() 函式對 問題 端點發出 GET 請求。例如,您可以執行以下命令來檢索第一頁上的第一個問題

import requests

url = "https://api.github.com/repos/huggingface/datasets/issues?page=1&per_page=1"
response = requests.get(url)

response 物件包含有關請求的大量有用資訊,包括 HTTP 狀態程式碼

response.status_code
200

其中 200 狀態表示請求已成功(您可以在 此處 找到可能的 HTTP 狀態程式碼列表)。但是,我們真正感興趣的是 *有效載荷*,可以透過各種格式訪問,例如位元組、字串或 JSON。由於我們知道我們的問題是 JSON 格式,因此讓我們檢查有效載荷,如下所示

response.json()
[{'url': 'https://api.github.com/repos/huggingface/datasets/issues/2792',
  'repository_url': 'https://api.github.com/repos/huggingface/datasets',
  'labels_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/labels{/name}',
  'comments_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/comments',
  'events_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/events',
  'html_url': 'https://github.com/huggingface/datasets/pull/2792',
  'id': 968650274,
  'node_id': 'MDExOlB1bGxSZXF1ZXN0NzEwNzUyMjc0',
  'number': 2792,
  'title': 'Update GooAQ',
  'user': {'login': 'bhavitvyamalik',
   'id': 19718818,
   'node_id': 'MDQ6VXNlcjE5NzE4ODE4',
   'avatar_url': 'https://avatars.githubusercontent.com/u/19718818?v=4',
   'gravatar_id': '',
   'url': 'https://api.github.com/users/bhavitvyamalik',
   'html_url': 'https://github.com/bhavitvyamalik',
   'followers_url': 'https://api.github.com/users/bhavitvyamalik/followers',
   'following_url': 'https://api.github.com/users/bhavitvyamalik/following{/other_user}',
   'gists_url': 'https://api.github.com/users/bhavitvyamalik/gists{/gist_id}',
   'starred_url': 'https://api.github.com/users/bhavitvyamalik/starred{/owner}{/repo}',
   'subscriptions_url': 'https://api.github.com/users/bhavitvyamalik/subscriptions',
   'organizations_url': 'https://api.github.com/users/bhavitvyamalik/orgs',
   'repos_url': 'https://api.github.com/users/bhavitvyamalik/repos',
   'events_url': 'https://api.github.com/users/bhavitvyamalik/events{/privacy}',
   'received_events_url': 'https://api.github.com/users/bhavitvyamalik/received_events',
   'type': 'User',
   'site_admin': False},
  'labels': [],
  'state': 'open',
  'locked': False,
  'assignee': None,
  'assignees': [],
  'milestone': None,
  'comments': 1,
  'created_at': '2021-08-12T11:40:18Z',
  'updated_at': '2021-08-12T12:31:17Z',
  'closed_at': None,
  'author_association': 'CONTRIBUTOR',
  'active_lock_reason': None,
  'pull_request': {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/2792',
   'html_url': 'https://github.com/huggingface/datasets/pull/2792',
   'diff_url': 'https://github.com/huggingface/datasets/pull/2792.diff',
   'patch_url': 'https://github.com/huggingface/datasets/pull/2792.patch'},
  'body': '[GooAQ](https://github.com/allenai/gooaq) dataset was recently updated after splits were added for the same. This PR contains new updated GooAQ with train/val/test splits and updated README as well.',
  'performed_via_github_app': None}]

哇,這真是很多資訊!我們可以看到有用的欄位,例如 titlebodynumber,它們描述了問題,以及有關開啟問題的人的資訊。

✏️ **試試看!** 點選上面 JSON 有效載荷中的幾個 URL,以瞭解每個 GitHub 問題連結到的資訊型別。

如 GitHub 文件 所述,未經身份驗證的請求每小時限制為 60 次請求。雖然您可以增加 per_page 查詢引數以減少發出的請求次數,但您仍然會在任何問題數超過幾千個的儲存庫上遇到速率限制。因此,您應該按照 GitHub 的 說明 建立一個 *個人訪問令牌*,以便您可以將速率限制提高到每小時 5,000 次請求。擁有令牌後,您可以將其作為請求標頭的一部分包含在內

GITHUB_TOKEN = xxx  # Copy your GitHub token here
headers = {"Authorization": f"token {GITHUB_TOKEN}"}

⚠️ 不要在包含貼上的 GITHUB_TOKEN 的筆記本中共享。建議您在執行完最後一個單元格後將其刪除,以避免意外洩露此資訊。更重要的是,將令牌儲存在 *。env* 檔案中,並使用 python-dotenv 將其自動載入為您環境變數。

現在我們有了訪問令牌,讓我們建立一個可以從 GitHub 儲存庫下載所有問題的函式

import time
import math
from pathlib import Path
import pandas as pd
from tqdm.notebook import tqdm


def fetch_issues(
    owner="huggingface",
    repo="datasets",
    num_issues=10_000,
    rate_limit=5_000,
    issues_path=Path("."),
):
    if not issues_path.is_dir():
        issues_path.mkdir(exist_ok=True)

    batch = []
    all_issues = []
    per_page = 100  # Number of issues to return per page
    num_pages = math.ceil(num_issues / per_page)
    base_url = "https://api.github.com/repos"

    for page in tqdm(range(num_pages)):
        # Query with state=all to get both open and closed issues
        query = f"issues?page={page}&per_page={per_page}&state=all"
        issues = requests.get(f"{base_url}/{owner}/{repo}/{query}", headers=headers)
        batch.extend(issues.json())

        if len(batch) > rate_limit and len(all_issues) < num_issues:
            all_issues.extend(batch)
            batch = []  # Flush batch for next time period
            print(f"Reached GitHub rate limit. Sleeping for one hour ...")
            time.sleep(60 * 60 + 1)

    all_issues.extend(batch)
    df = pd.DataFrame.from_records(all_issues)
    df.to_json(f"{issues_path}/{repo}-issues.jsonl", orient="records", lines=True)
    print(
        f"Downloaded all the issues for {repo}! Dataset stored at {issues_path}/{repo}-issues.jsonl"
    )

現在,當我們呼叫 fetch_issues() 時,它將分批下載所有問題,以避免超過 GitHub 對每小時請求次數的限制;結果將儲存在 *repository_name-issues.jsonl* 檔案中,每行都是一個表示問題的 JSON 物件。讓我們使用此函式來獲取 🤗 資料集的所有問題

# Depending on your internet connection, this can take several minutes to run...
fetch_issues()

下載問題後,我們可以使用我們在 第 2 節 中學習到的新技能在本地載入它們

issues_dataset = load_dataset("json", data_files="datasets-issues.jsonl", split="train")
issues_dataset
Dataset({
    features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'timeline_url', 'performed_via_github_app'],
    num_rows: 3019
})

太棒了,我們從頭開始建立了第一個資料集!但是,為什麼有幾千個問題,而 🤗 資料集儲存庫的 問題標籤 總共只顯示了大約 1,000 個問題 🤔?如 GitHub 文件 所述,這是因為我們也下載了所有拉取請求

GitHub 的 REST API v3 將每個拉取請求都視為一個問題,但並非每個問題都是拉取請求。因此,“問題”端點可能會在響應中返回問題和拉取請求。您可以透過 pull_request 鍵來識別拉取請求。請注意,從“問題”端點返回的拉取請求的 id 將是一個問題 id。

由於問題和拉取請求的內容非常不同,因此讓我們進行一些小的預處理,使我們能夠區分它們。

清理資料

上面來自 GitHub 文件的程式碼片段告訴我們,可以使用 pull_request 列來區分問題和拉取請求。讓我們看看隨機樣本,看看有什麼區別。就像我們在 第 3 節 中所做的那樣,我們將連結 Dataset.shuffle()Dataset.select() 來建立隨機樣本,然後將 html_urlpull_request 列壓縮在一起,以便我們可以比較各種 URL

sample = issues_dataset.shuffle(seed=666).select(range(3))

# Print out the URL and pull request entries
for url, pr in zip(sample["html_url"], sample["pull_request"]):
    print(f">> URL: {url}")
    print(f">> Pull request: {pr}\n")
>> URL: https://github.com/huggingface/datasets/pull/850
>> Pull request: {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/850', 'html_url': 'https://github.com/huggingface/datasets/pull/850', 'diff_url': 'https://github.com/huggingface/datasets/pull/850.diff', 'patch_url': 'https://github.com/huggingface/datasets/pull/850.patch'}

>> URL: https://github.com/huggingface/datasets/issues/2773
>> Pull request: None

>> URL: https://github.com/huggingface/datasets/pull/783
>> Pull request: {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/783', 'html_url': 'https://github.com/huggingface/datasets/pull/783', 'diff_url': 'https://github.com/huggingface/datasets/pull/783.diff', 'patch_url': 'https://github.com/huggingface/datasets/pull/783.patch'}

在這裡我們可以看到,每個拉取請求都與各種 URL 相關聯,而普通問題則具有 None 條目。我們可以使用此區別來建立一個新的 is_pull_request 列,該列檢查 pull_request 欄位是否為 None

issues_dataset = issues_dataset.map(
    lambda x: {"is_pull_request": False if x["pull_request"] is None else True}
)

✏️ **試試看!** 計算關閉 🤗 資料集問題所需時間的平均值。您可能會發現 Dataset.filter() 函式對過濾掉拉取請求和開放問題很有用,並且可以使用 Dataset.set_format() 函式將資料集轉換為 DataFrame,以便您可以輕鬆地操作 created_atclosed_at 時間戳。為獲得額外加分,請計算關閉拉取請求所需時間的平均值。

雖然我們可以透過刪除或重新命名某些列來進一步清理資料集,但通常的做法是在此階段儘可能保持資料集的“原始”狀態,以便它可以輕鬆地用於多個應用程式。

在我們把資料集推送到 Hugging Face Hub 之前,讓我們處理一個它缺少的東西:與每個問題和拉取請求相關的評論。我們將在下一步新增它們——你猜對了——使用 GitHub REST API!

擴充資料集

如以下螢幕截圖所示,與問題或拉取請求相關的評論提供了豐富的 資訊來源,特別是如果我們有興趣構建一個搜尋引擎來回答使用者關於庫的查詢。

Comments associated with an issue about 🤗 Datasets.

GitHub REST API 提供了一個 Comments 端點,它返回與問題編號相關的所有評論。讓我們測試一下端點,看看它返回了什麼。

issue_number = 2792
url = f"https://api.github.com/repos/huggingface/datasets/issues/{issue_number}/comments"
response = requests.get(url, headers=headers)
response.json()
[{'url': 'https://api.github.com/repos/huggingface/datasets/issues/comments/897594128',
  'html_url': 'https://github.com/huggingface/datasets/pull/2792#issuecomment-897594128',
  'issue_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792',
  'id': 897594128,
  'node_id': 'IC_kwDODunzps41gDMQ',
  'user': {'login': 'bhavitvyamalik',
   'id': 19718818,
   'node_id': 'MDQ6VXNlcjE5NzE4ODE4',
   'avatar_url': 'https://avatars.githubusercontent.com/u/19718818?v=4',
   'gravatar_id': '',
   'url': 'https://api.github.com/users/bhavitvyamalik',
   'html_url': 'https://github.com/bhavitvyamalik',
   'followers_url': 'https://api.github.com/users/bhavitvyamalik/followers',
   'following_url': 'https://api.github.com/users/bhavitvyamalik/following{/other_user}',
   'gists_url': 'https://api.github.com/users/bhavitvyamalik/gists{/gist_id}',
   'starred_url': 'https://api.github.com/users/bhavitvyamalik/starred{/owner}{/repo}',
   'subscriptions_url': 'https://api.github.com/users/bhavitvyamalik/subscriptions',
   'organizations_url': 'https://api.github.com/users/bhavitvyamalik/orgs',
   'repos_url': 'https://api.github.com/users/bhavitvyamalik/repos',
   'events_url': 'https://api.github.com/users/bhavitvyamalik/events{/privacy}',
   'received_events_url': 'https://api.github.com/users/bhavitvyamalik/received_events',
   'type': 'User',
   'site_admin': False},
  'created_at': '2021-08-12T12:21:52Z',
  'updated_at': '2021-08-12T12:31:17Z',
  'author_association': 'CONTRIBUTOR',
  'body': "@albertvillanova my tests are failing here:\r\n```\r\ndataset_name = 'gooaq'\r\n\r\n    def test_load_dataset(self, dataset_name):\r\n        configs = self.dataset_tester.load_all_configs(dataset_name, is_local=True)[:1]\r\n>       self.dataset_tester.check_load_dataset(dataset_name, configs, is_local=True, use_local_dummy_data=True)\r\n\r\ntests/test_dataset_common.py:234: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\ntests/test_dataset_common.py:187: in check_load_dataset\r\n    self.parent.assertTrue(len(dataset[split]) > 0)\r\nE   AssertionError: False is not true\r\n```\r\nWhen I try loading dataset on local machine it works fine. Any suggestions on how can I avoid this error?",
  'performed_via_github_app': None}]

我們可以看到評論儲存在 body 欄位中,所以讓我們編寫一個簡單的函式,它透過從 response.json() 中的每個元素中選取 body 內容來返回與問題相關的所有評論。

def get_comments(issue_number):
    url = f"https://api.github.com/repos/huggingface/datasets/issues/{issue_number}/comments"
    response = requests.get(url, headers=headers)
    return [r["body"] for r in response.json()]


# Test our function works as expected
get_comments(2792)
["@albertvillanova my tests are failing here:\r\n```\r\ndataset_name = 'gooaq'\r\n\r\n    def test_load_dataset(self, dataset_name):\r\n        configs = self.dataset_tester.load_all_configs(dataset_name, is_local=True)[:1]\r\n>       self.dataset_tester.check_load_dataset(dataset_name, configs, is_local=True, use_local_dummy_data=True)\r\n\r\ntests/test_dataset_common.py:234: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\ntests/test_dataset_common.py:187: in check_load_dataset\r\n    self.parent.assertTrue(len(dataset[split]) > 0)\r\nE   AssertionError: False is not true\r\n```\r\nWhen I try loading dataset on local machine it works fine. Any suggestions on how can I avoid this error?"]

看起來不錯,所以讓我們使用 Dataset.map() 為我們資料集中每個問題新增一個新的 comments 列。

# Depending on your internet connection, this can take a few minutes...
issues_with_comments_dataset = issues_dataset.map(
    lambda x: {"comments": get_comments(x["number"])}
)

最後一步是將我們的資料集推送到 Hub。讓我們看看如何做到這一點。

將資料集上傳到 Hugging Face Hub

現在我們有了擴充後的資料集,是時候將它推送到 Hub,以便與社群分享了!上傳資料集非常簡單:就像 🤗 Transformers 中的模型和分詞器一樣,我們可以使用 push_to_hub() 方法來推送資料集。為此,我們需要一個身份驗證令牌,可以透過首先使用 notebook_login() 函式登入 Hugging Face Hub 來獲得。

from huggingface_hub import notebook_login

notebook_login()

這將建立一個視窗,您可以在其中輸入您的使用者名稱和密碼,API 令牌將儲存在 ~/.huggingface/token 中。如果您在終端中執行程式碼,您可以透過 CLI 代替登入。

huggingface-cli login

完成此操作後,我們可以透過執行以下命令上傳我們的資料集:

issues_with_comments_dataset.push_to_hub("github-issues")

從這裡,任何人都可以透過簡單地向 load_dataset() 提供儲存庫 ID 作為 path 引數來下載資料集。

remote_dataset = load_dataset("lewtun/github-issues", split="train")
remote_dataset
Dataset({
    features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
    num_rows: 2855
})

酷,我們已經將資料集推送到 Hub,並且它可供其他人使用!還有一件重要的事情要做:新增一個資料集卡片,解釋語料庫是如何建立的,併為社群提供其他有用的資訊。

💡 您也可以透過使用 huggingface-cli 和一些 Git 技巧,直接從終端將資料集上傳到 Hugging Face Hub。有關如何執行此操作的詳細資訊,請參閱 🤗 資料集指南

建立資料集卡片

記錄良好的資料集更有可能對其他人(包括未來的自己)有用,因為它們提供了上下文,使使用者能夠決定資料集是否與其任務相關,並評估使用資料集時可能存在的任何偏差或風險。

在 Hugging Face Hub 上,此資訊儲存在每個資料集儲存庫的 README.md 檔案中。在建立此檔案之前,您應該執行兩個主要步驟。

  1. 使用 datasets-tagging 應用程式 以 YAML 格式建立元資料標籤。這些標籤用於 Hugging Face Hub 上的各種搜尋功能,並確保您的資料集可以輕鬆地被社群成員找到。由於我們在這裡建立了一個自定義資料集,您需要克隆 datasets-tagging 儲存庫並在本地執行該應用程式。以下是介面的樣子。
The 'datasets-tagging' interface.
  1. 閱讀 🤗 資料集指南,瞭解如何建立資訊豐富的 資料集卡片,並將其用作模板。

您可以在 Hub 上直接建立 README.md 檔案,您可以在 lewtun/github-issues 資料集儲存庫中找到一個模板資料集卡片。以下是一張填寫完整的資料集卡片的螢幕截圖。

A dataset card.

✏️ 試試吧!使用 dataset-tagging 應用程式和 🤗 資料集指南 來完成 GitHub 問題資料集的 README.md 檔案。

就是這樣!我們在本節中看到,建立一個好的資料集可能相當複雜,但幸運的是,上傳它並與社群分享並不難。在下一節中,我們將使用我們的新資料集使用 🤗 Datasets 建立一個語義搜尋引擎,它可以將問題與最相關的問題和評論進行匹配。

✏️ 試試吧!按照我們在本節中執行的步驟,為您最喜歡的開源庫建立一個 GitHub 問題資料集(當然,選擇 🤗 Datasets 以外的庫)。作為額外獎勵,微調一個多標籤分類器來預測 labels 欄位中存在的標籤。

© . This site is unofficial and not affiliated with Hugging Face, Inc.