並獲得增強文件體驗的訪問許可權
開始使用
是時候切片切塊了
大多數情況下,您使用的資料不會完美地準備好用於訓練模型。在本節中,我們將探索 🤗 Datasets 提供的各種功能,用於清理您的資料集。
切片切塊我們的資料
類似於 Pandas,🤗 Datasets 提供了多個函式來操作 Dataset 和 DatasetDict 物件的內容。我們已經在 第 3 章 中遇到了 Dataset.map() 方法,在本節中,我們將探索其他可用的函式。
在本示例中,我們將使用 藥物評論資料集,該資料集託管在 UC Irvine 機器學習資源庫 中,其中包含患者對各種藥物的評論,以及治療的病症和患者滿意度的 10 星評級。
首先,我們需要下載並解壓縮資料,這可以使用 wget 和 unzip 命令完成
!wget "https://archive.ics.uci.edu/ml/machine-learning-databases/00462/drugsCom_raw.zip"
!unzip drugsCom_raw.zip由於 TSV 只是 CSV 的變體,它使用製表符而不是逗號作為分隔符,我們可以透過使用 csv 載入指令碼並將 delimiter 引數指定為 load_dataset() 函式中的如下所示來載入這些檔案
from datasets import load_dataset
data_files = {"train": "drugsComTrain_raw.tsv", "test": "drugsComTest_raw.tsv"}
# \t is the tab character in Python
drug_dataset = load_dataset("csv", data_files=data_files, delimiter="\t")在進行任何型別的資料分析時,一個好的做法是獲取一個小型的隨機樣本,以便快速瞭解您正在處理的資料型別。在 🤗 Datasets 中,我們可以透過將 Dataset.shuffle() 和 Dataset.select() 函式連結在一起來建立隨機樣本
drug_sample = drug_dataset["train"].shuffle(seed=42).select(range(1000))
# Peek at the first few examples
drug_sample[:3]{'Unnamed: 0': [87571, 178045, 80482],
'drugName': ['Naproxen', 'Duloxetine', 'Mobic'],
'condition': ['Gout, Acute', 'ibromyalgia', 'Inflammatory Conditions'],
'review': ['"like the previous person mention, I'm a strong believer of aleve, it works faster for my gout than the prescription meds I take. No more going to the doctor for refills.....Aleve works!"',
'"I have taken Cymbalta for about a year and a half for fibromyalgia pain. It is great\r\nas a pain reducer and an anti-depressant, however, the side effects outweighed \r\nany benefit I got from it. I had trouble with restlessness, being tired constantly,\r\ndizziness, dry mouth, numbness and tingling in my feet, and horrible sweating. I am\r\nbeing weaned off of it now. Went from 60 mg to 30mg and now to 15 mg. I will be\r\noff completely in about a week. The fibro pain is coming back, but I would rather deal with it than the side effects."',
'"I have been taking Mobic for over a year with no side effects other than an elevated blood pressure. I had severe knee and ankle pain which completely went away after taking Mobic. I attempted to stop the medication however pain returned after a few days."'],
'rating': [9.0, 3.0, 10.0],
'date': ['September 2, 2015', 'November 7, 2011', 'June 5, 2013'],
'usefulCount': [36, 13, 128]}請注意,我們已將 Dataset.shuffle() 中的種子固定,以實現可重複性。Dataset.select() 期待一個索引的迭代器,因此我們傳遞了 range(1000) 來從隨機排序的資料集中獲取前 1000 個示例。從這個樣本中,我們已經可以看出資料集中的一些怪癖
Unnamed: 0列看起來很像每個患者的匿名 ID。condition列包含大小寫混合的標籤。- 評論的長度各不相同,包含 Python 行分隔符 (
\r\n) 以及 HTML 字元程式碼,如&\#039;。
讓我們看看如何使用 🤗 Datasets 來解決這些問題。為了測試 Unnamed: 0 列的患者 ID 假設,我們可以使用 Dataset.unique() 函式來驗證 ID 的數量是否與每個拆分的行數相匹配
for split in drug_dataset.keys():
assert len(drug_dataset[split]) == len(drug_dataset[split].unique("Unnamed: 0"))這似乎證實了我們的假設,因此讓我們透過將 Unnamed: 0 列重新命名為更易於理解的名稱來清理資料集。我們可以使用 DatasetDict.rename_column() 函式來一次性重新命名兩個拆分中的列
drug_dataset = drug_dataset.rename_column(
original_column_name="Unnamed: 0", new_column_name="patient_id"
)
drug_datasetDatasetDict({
train: Dataset({
features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount'],
num_rows: 161297
})
test: Dataset({
features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount'],
num_rows: 53766
})
})✏️ 試一試! 使用 Dataset.unique() 函式在訓練集和測試集中查詢唯一藥物和病症的數量。
接下來,讓我們使用 Dataset.map() 規範化所有 condition 標籤。正如我們在 第 3 章 中對分詞所做的那樣,我們可以定義一個簡單的函式,該函式可以應用於 drug_dataset 中每個拆分的全部行
def lowercase_condition(example):
return {"condition": example["condition"].lower()}
drug_dataset.map(lowercase_condition)AttributeError: 'NoneType' object has no attribute 'lower'哦,不,我們的對映函式遇到了問題!從錯誤中可以推斷出 condition 列中的一些條目是 None,由於它們不是字串,因此無法轉換為小寫。讓我們使用 Dataset.filter() 刪除這些行,它的工作原理類似於 Dataset.map(),並期待接收資料集單個示例的函式。與其像這樣編寫顯式函式
def filter_nones(x):
return x["condition"] is not None然後執行 drug_dataset.filter(filter_nones),我們可以使用lambda 函式在一行中完成此操作。在 Python 中,lambda 函式是您可以在不顯式命名的情況下定義的小函式。它們採用以下通用形式
lambda <arguments> : <expression>其中 lambda 是 Python 的特殊關鍵字,<arguments> 是定義函式輸入的逗號分隔值列表/集合,<expression> 表示您希望執行的操作。例如,我們可以定義一個簡單的 lambda 函式,該函式對一個數字進行平方,如下所示
lambda x : x * x要將此函式應用於輸入,我們需要將其與輸入一起放在括號中
(lambda x: x * x)(3)9同樣,我們可以透過用逗號分隔多個引數來定義具有多個引數的 lambda 函式。例如,我們可以計算三角形的面積,如下所示
(lambda base, height: 0.5 * base * height)(4, 8)16.0當您想定義小的、一次性使用的函式時,lambda 函式非常方便(有關它們的更多資訊,我們建議閱讀 Andre Burgaud 編寫的出色的 Real Python 教程)。在 🤗 Datasets 上下文中,我們可以使用 lambda 函式來定義簡單的對映和過濾操作,因此讓我們使用此技巧來消除資料集中的 None 條目
drug_dataset = drug_dataset.filter(lambda x: x["condition"] is not None)刪除了 None 條目後,我們可以規範化 condition 列
drug_dataset = drug_dataset.map(lowercase_condition)
# Check that lowercasing worked
drug_dataset["train"]["condition"][:3]['left ventricular dysfunction', 'adhd', 'birth control']它起作用了!現在我們已經清理了標籤,讓我們看看如何清理評論本身。
建立新列
無論何時處理客戶評論,一個好的做法是檢查每條評論的詞語數量。評論可能只是一個簡單的詞語,例如“很棒!”,也可能是一篇包含數千個詞語的完整文章,具體取決於用例,您需要以不同的方式處理這些極端情況。為了計算每條評論中的詞語數量,我們將使用基於將每個文字按空格分隔的粗略啟發式方法。
讓我們定義一個簡單的函式,該函式計算每條評論中的詞語數量
def compute_review_length(example):
return {"review_length": len(example["review"].split())}與 lowercase_condition() 函式不同,compute_review_length() 返回一個字典,其鍵不對應於資料集中的任何列名。在這種情況下,當 compute_review_length() 傳遞給 Dataset.map() 時,它將應用於資料集中的全部行,以建立一個新的 review_length 列
drug_dataset = drug_dataset.map(compute_review_length)
# Inspect the first training example
drug_dataset["train"][0]{'patient_id': 206461,
'drugName': 'Valsartan',
'condition': 'left ventricular dysfunction',
'review': '"It has no side effect, I take it in combination of Bystolic 5 Mg and Fish Oil"',
'rating': 9.0,
'date': 'May 20, 2012',
'usefulCount': 27,
'review_length': 17}正如預期,我們可以看到訓練集中添加了一個review_length列。我們可以使用Dataset.sort()對這列新資料進行排序,以檢視極值的樣子。
drug_dataset["train"].sort("review_length")[:3]{'patient_id': [103488, 23627, 20558],
'drugName': ['Loestrin 21 1 / 20', 'Chlorzoxazone', 'Nucynta'],
'condition': ['birth control', 'muscle spasm', 'pain'],
'review': ['"Excellent."', '"useless"', '"ok"'],
'rating': [10.0, 1.0, 6.0],
'date': ['November 4, 2008', 'March 24, 2017', 'August 20, 2016'],
'usefulCount': [5, 2, 10],
'review_length': [1, 1, 1]}正如我們所懷疑的,有些評論只包含一個詞,雖然這對於情感分析來說可能沒問題,但如果我們想預測條件,這將不會提供任何資訊。
🙋 為資料集新增新列的另一種方法是使用Dataset.add_column()函式。這使您可以將列作為 Python 列表或 NumPy 陣列提供,並且在Dataset.map()不適合您的分析的情況下非常方便。
讓我們使用Dataset.filter()函式刪除包含少於 30 個詞的評論。與我們對condition列所做的類似,我們可以透過要求評論的長度超過此閾值來過濾掉非常短的評論。
drug_dataset = drug_dataset.filter(lambda x: x["review_length"] > 30)
print(drug_dataset.num_rows){'train': 138514, 'test': 46108}如您所見,這已經從我們的原始訓練集和測試集中刪除了大約 15% 的評論。
✏️ **試試看!** 使用Dataset.sort()函式檢視包含最多詞的評論。請參閱文件,檢視需要使用哪個引數按降序排列評論長度。
我們需要處理的最後一件事是我們的評論中存在 HTML 字元程式碼。我們可以使用 Python 的html模組來取消轉義這些字元,如下所示
import html
text = "I'm a transformer called BERT"
html.unescape(text)"I'm a transformer called BERT"我們將使用Dataset.map()取消轉義語料庫中的所有 HTML 字元。
drug_dataset = drug_dataset.map(lambda x: {"review": html.unescape(x["review"])})如您所見,Dataset.map()方法對於處理資料非常有用——而且我們甚至還沒有觸及它所能做的一切的皮毛!
map() 方法的超能力
Dataset.map()方法接受一個batched引數,如果設定為True,它將一次將一批示例傳送到對映函式(批次大小是可配置的,但預設值為 1,000)。例如,之前取消轉義所有 HTML 的對映函式需要一些時間才能執行(您可以從進度條中讀取所花費的時間)。我們可以透過同時處理多個元素來加快速度,使用列表推導。
當您指定batched=True時,函式會接收一個包含資料集欄位的字典,但每個值現在是一個值列表,而不僅僅是一個單值。Dataset.map()的返回值應該是相同的:一個包含要更新或新增到資料集中的欄位的字典,以及一個值列表。例如,以下是如何取消轉義所有 HTML 字元的另一種方法,但使用batched=True
new_drug_dataset = drug_dataset.map(
lambda x: {"review": [html.unescape(o) for o in x["review"]]}, batched=True
)如果您在筆記本中執行此程式碼,您會看到此命令的執行速度比之前的命令快得多。這不是因為我們的評論已經取消了 HTML 轉義——如果您重新執行上一節中的指令(不使用batched=True),它將花費與之前相同的時間。這是因為列表推導通常比在for迴圈中執行相同的程式碼更快,而且我們還透過一次訪問大量元素而不是一個一個地訪問來提高效能。
在第 6 章中,我們將使用batched=True的Dataset.map()對於解鎖“快速”分詞器的速度至關重要,這些分詞器可以快速分詞大量文字。例如,要使用快速分詞器分詞所有藥物評論,我們可以使用這樣的函式
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
def tokenize_function(examples):
return tokenizer(examples["review"], truncation=True)正如您在第 3 章中看到的,我們可以將一個或多個示例傳遞給分詞器,因此我們可以使用或不使用batched=True來使用此函式。讓我們藉此機會比較不同選項的效能。在筆記本中,您可以透過在要測量的程式碼行之前新增%time來計時一行指令。
%time tokenized_dataset = drug_dataset.map(tokenize_function, batched=True)您也可以透過在單元格開頭放置%%time來計時整個單元格。在我們執行此操作的硬體上,它顯示此指令為 10.8 秒(這是“Wall time”後面顯示的數字)。
✏️ **試試看!** 使用和不使用batched=True執行相同的指令,然後嘗試使用慢速分詞器(在AutoTokenizer.from_pretrained()方法中新增use_fast=False),這樣您就可以看到在您的硬體上獲得的數字。
以下是我們在使用和不使用批處理的情況下,使用快速和慢速分詞器獲得的結果
| 選項 | 快速分詞器 | 慢速分詞器 |
|---|---|---|
batched=True | 10.8s | 4分鐘41秒 |
batched=False | 59.2s | 5分鐘3秒 |
這意味著使用帶有batched=True選項的快速分詞器比不使用批處理的慢速分詞器快 30 倍——這真是太棒了!這就是快速分詞器在使用AutoTokenizer時成為預設分詞器(以及為什麼它們被稱為“快速”)的主要原因。它們能夠實現如此大的加速,因為幕後分詞程式碼是用 Rust 編寫的,這是一種易於並行化程式碼執行的語言。
並行化也是快速分詞器在使用批處理時實現近 6 倍加速的原因:您無法並行化單個分詞操作,但當您想要同時分詞大量文字時,您只需將執行拆分為多個程序,每個程序負責自己的文字。
Dataset.map()本身也有一些並行化功能。由於它們沒有 Rust 的支援,它們不會讓慢速分詞器趕上快速分詞器,但它們仍然可以提供幫助(尤其是如果您使用的是沒有快速版本的分詞器)。要啟用多程序,請使用num_proc引數並在呼叫Dataset.map()時指定要使用的程序數。
slow_tokenizer = AutoTokenizer.from_pretrained("bert-base-cased", use_fast=False)
def slow_tokenize_function(examples):
return slow_tokenizer(examples["review"], truncation=True)
tokenized_dataset = drug_dataset.map(slow_tokenize_function, batched=True, num_proc=8)您可以嘗試進行一些計時,以確定要使用的最佳程序數;在我們看來,8 似乎產生了最佳的加速效果。以下是我們在使用和不使用多程序的情況下獲得的數字
| 選項 | 快速分詞器 | 慢速分詞器 |
|---|---|---|
batched=True | 10.8s | 4分鐘41秒 |
batched=False | 59.2s | 5分鐘3秒 |
batched=True, num_proc=8 | 6.52s | 41.3s |
batched=False, num_proc=8 | 9.49s | 45.2s |
對於慢速分詞器來說,這些結果要合理得多,但快速分詞器的效能也得到了顯著提升。但是請注意,情況並非總是如此——對於除 8 之外的num_proc值,我們的測試表明,不使用該選項而使用batched=True更快。一般來說,我們不建議將 Python 多程序用於帶有batched=True的快速分詞器。
使用num_proc來加速您的處理通常是一個好主意,只要您使用的函式本身沒有執行某種多程序。
所有這些功能都壓縮到一個方法中,這已經很驚人了,但還有更多!使用Dataset.map()和batched=True,您可以更改資料集中的元素數量。這在許多情況下非常有用,您希望從一個示例建立多個訓練特徵,並且在第 7 章中,我們需要在幾個 NLP 任務的預處理過程中執行此操作。
💡 在機器學習中,一個示例通常被定義為我們提供給模型的特徵集。在某些情況下,這些特徵將是Dataset中的一組列,但在其他情況下(比如這裡和問答),可以從單個示例中提取多個特徵,並且屬於單個列。
讓我們看看它是如何工作的!在這裡,我們將分詞我們的示例並將它們截斷為最大長度為 128,但我們將要求分詞器返回所有文字塊,而不僅僅是第一個塊。這可以透過return_overflowing_tokens=True來實現。
def tokenize_and_split(examples):
return tokenizer(
examples["review"],
truncation=True,
max_length=128,
return_overflowing_tokens=True,
)讓我們在對整個資料集使用Dataset.map()之前,在一個示例上測試一下。
result = tokenize_and_split(drug_dataset["train"][0])
[len(inp) for inp in result["input_ids"]][128, 49]因此,訓練集中的第一個示例變成了兩個特徵,因為它被分詞成超過我們指定的最大令牌數:第一個長度為 128,第二個長度為 49。現在讓我們對資料集的所有元素執行此操作!
tokenized_dataset = drug_dataset.map(tokenize_and_split, batched=True)ArrowInvalid: Column 1 named condition expected length 1463 but got length 1000哦不!這沒有成功!為什麼?檢視錯誤訊息將為我們提供線索:其中一列的長度不匹配,一列的長度為 1,463,另一列的長度為 1,000。如果您查看了Dataset.map()的文件,您可能會回想起,它是傳遞給對映函式的樣本數量;在這裡,這 1,000 個示例產生了 1,463 個新特徵,導致形狀錯誤。
問題是我們試圖混合兩個不同大小的不同資料集:drug_dataset列將具有特定數量的示例(錯誤中的 1,000),但我們正在構建的tokenized_dataset將具有更多示例(錯誤訊息中的 1,463;它大於 1,000,因為我們使用return_overflowing_tokens=True將長評論分詞成多個示例)。這對於Dataset來說是行不通的,因此我們需要刪除舊資料集中的列,或者將它們的大小與新資料集中的一致。我們可以使用remove_columns引數來執行前者
tokenized_dataset = drug_dataset.map(
tokenize_and_split, batched=True, remove_columns=drug_dataset["train"].column_names
)現在這可以正常工作了,沒有錯誤。我們可以透過比較長度來檢查我們的新資料集比原始資料集多出許多元素。
len(tokenized_dataset["train"]), len(drug_dataset["train"])(206772, 138514)我們提到過,我們也可以透過使舊列的大小與新列的大小相同來解決長度不匹配問題。為此,我們需要分詞器在設定return_overflowing_tokens=True時返回的overflow_to_sample_mapping欄位。它為我們提供了一個從新特徵索引到它起源的樣本索引的對映。使用它,我們可以將原始資料集中存在的每個鍵與一個具有正確大小的值列表相關聯,方法是重複每個示例的值,重複的次數與它生成的新特徵一樣多。
def tokenize_and_split(examples):
result = tokenizer(
examples["review"],
truncation=True,
max_length=128,
return_overflowing_tokens=True,
)
# Extract mapping between new and old indices
sample_map = result.pop("overflow_to_sample_mapping")
for key, values in examples.items():
result[key] = [values[i] for i in sample_map]
return result我們可以看到它與Dataset.map()一起工作,而無需我們刪除舊列。
tokenized_dataset = drug_dataset.map(tokenize_and_split, batched=True)
tokenized_datasetDatasetDict({
train: Dataset({
features: ['attention_mask', 'condition', 'date', 'drugName', 'input_ids', 'patient_id', 'rating', 'review', 'review_length', 'token_type_ids', 'usefulCount'],
num_rows: 206772
})
test: Dataset({
features: ['attention_mask', 'condition', 'date', 'drugName', 'input_ids', 'patient_id', 'rating', 'review', 'review_length', 'token_type_ids', 'usefulCount'],
num_rows: 68876
})
})我們得到了與之前相同的訓練特徵數量,但在這裡我們保留了所有舊欄位。如果您需要它們在應用模型後進行一些後處理,您可能想使用這種方法。
您現在已經瞭解瞭如何使用 🤗 Datasets 以多種方式對資料集進行預處理。雖然 🤗 Datasets 的處理函式將涵蓋您的大部分模型訓練需求,但可能有時您需要切換到 Pandas 以訪問更強大的功能,例如DataFrame.groupby()或視覺化的高階 API。幸運的是,🤗 Datasets 被設計為可與 Pandas、NumPy、PyTorch、TensorFlow 和 JAX 等庫互操作。讓我們看看它是如何工作的。
從 Dataset 到 DataFrame 以及返回
為了實現不同第三方庫之間的轉換,🤗 Datasets 提供了一個Dataset.set_format() 函式。此函式只改變了資料集的輸出格式,因此你可以輕鬆地切換到另一種格式,而不會影響底層的資料格式(即 Apache Arrow)。格式化是在原地完成的。為了演示,我們將資料集轉換為 Pandas
drug_dataset.set_format("pandas")現在,當我們訪問資料集的元素時,我們會得到一個pandas.DataFrame,而不是字典
drug_dataset["train"][:3]| patient_id | drugName | condition | review | rating | date | usefulCount | review_length | |
|---|---|---|---|---|---|---|---|---|
| 0 | 95260 | Guanfacine | adhd | "My son is halfway through his fourth week of Intuniv..." | 8.0 | April 27, 2010 | 192 | 141 |
| 1 | 92703 | Lybrel | birth control | "I used to take another oral contraceptive, which had 21 pill cycle, and was very happy- very light periods, max 5 days, no other side effects..." | 5.0 | December 14, 2009 | 17 | 134 |
| 2 | 138000 | Ortho Evra | birth control | "This is my first time using any form of birth control..." | 8.0 | November 3, 2015 | 10 | 89 |
讓我們為整個訓練集建立一個pandas.DataFrame,方法是選擇drug_dataset["train"]的所有元素
train_df = drug_dataset["train"][:]🚨 在幕後,Dataset.set_format() 改變了資料集__getitem__() dunder 方法的返回值格式。這意味著當我們想從"pandas" 格式的Dataset 中建立一個新的物件(如train_df)時,我們需要切片整個資料集來獲得一個pandas.DataFrame。你可以自己驗證drug_dataset["train"] 的型別是Dataset,而與輸出格式無關。
從這裡開始,我們可以使用所有我們想要的 Pandas 功能。例如,我們可以執行復雜的鏈式操作來計算condition 條目中的類分佈
frequencies = (
train_df["condition"]
.value_counts()
.to_frame()
.reset_index()
.rename(columns={"index": "condition", "condition": "frequency"})
)
frequencies.head()| condition | frequency | |
|---|---|---|
| 0 | birth control | 27655 |
| 1 | depression | 8023 |
| 2 | acne | 5209 |
| 3 | anxiety | 4991 |
| 4 | pain | 4744 |
一旦我們完成了 Pandas 分析,我們就可以始終使用Dataset.from_pandas() 函式建立一個新的Dataset 物件,如下所示
from datasets import Dataset
freq_dataset = Dataset.from_pandas(frequencies)
freq_datasetDataset({
features: ['condition', 'frequency'],
num_rows: 819
})✏️ **試試看!** 計算每個藥物的平均評分,並將結果儲存在一個新的Dataset 中。
這總結了我們對 🤗 Datasets 中各種預處理技術的探索。為了完善本節內容,讓我們建立一個驗證集,為訓練分類器做準備。在此之前,我們將drug_dataset 的輸出格式從"pandas" 重置為"arrow"
drug_dataset.reset_format()
建立驗證集
雖然我們有一個可以用於評估的測試集,但最好在開發過程中保留測試集,並建立一個單獨的驗證集。一旦你對模型在驗證集上的效能感到滿意,你就可以在測試集上進行最終的健全性檢查。此過程有助於降低過度擬合測試集並部署在真實世界資料上失敗的模型的風險。
🤗 Datasets 提供一個Dataset.train_test_split() 函式,該函式基於著名的scikit-learn 功能。讓我們使用它將我們的訓練集分割成train 和validation 分割(我們將seed 引數設定為可重複性)
drug_dataset_clean = drug_dataset["train"].train_test_split(train_size=0.8, seed=42)
# Rename the default "test" split to "validation"
drug_dataset_clean["validation"] = drug_dataset_clean.pop("test")
# Add the "test" set to our `DatasetDict`
drug_dataset_clean["test"] = drug_dataset["test"]
drug_dataset_cleanDatasetDict({
train: Dataset({
features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
num_rows: 110811
})
validation: Dataset({
features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
num_rows: 27703
})
test: Dataset({
features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
num_rows: 46108
})
})太好了,我們現在已經準備了一個數據集,可以用來訓練一些模型了!在第 5 節中,我們將向你展示如何將資料集上傳到 Hugging Face Hub,但現在讓我們透過檢視一些在本地機器上儲存資料集的方法來結束我們的分析。
儲存資料集
雖然 🤗 Datasets 會快取每個下載的資料集以及對其執行的操作,但有時你可能想要將資料集儲存到磁碟(例如,如果快取被刪除)。如以下表格所示,🤗 Datasets 提供了三個主要函式,以不同的格式儲存你的資料集
| 資料格式 | 函式 |
|---|---|
| Arrow | Dataset.save_to_disk() |
| CSV | Dataset.to_csv() |
| JSON | Dataset.to_json() |
例如,讓我們將清理後的資料集以 Arrow 格式儲存
drug_dataset_clean.save_to_disk("drug-reviews")這將建立一個具有以下結構的目錄
drug-reviews/
├── dataset_dict.json
├── test
│ ├── dataset.arrow
│ ├── dataset_info.json
│ └── state.json
├── train
│ ├── dataset.arrow
│ ├── dataset_info.json
│ ├── indices.arrow
│ └── state.json
└── validation
├── dataset.arrow
├── dataset_info.json
├── indices.arrow
└── state.json我們可以看到,每個分割都與自己的dataset.arrow 表相關聯,以及dataset_info.json 和state.json 中的一些元資料。你可以將 Arrow 格式視為一個專為構建高效能應用程式而最佳化的列和行的精美表格,這些應用程式可以處理和傳輸大型資料集。
儲存資料集後,我們可以使用load_from_disk() 函式載入它,如下所示
from datasets import load_from_disk
drug_dataset_reloaded = load_from_disk("drug-reviews")
drug_dataset_reloadedDatasetDict({
train: Dataset({
features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
num_rows: 110811
})
validation: Dataset({
features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
num_rows: 27703
})
test: Dataset({
features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
num_rows: 46108
})
})對於 CSV 和 JSON 格式,我們必須將每個分割儲存為一個單獨的檔案。一種方法是遍歷DatasetDict 物件中的鍵值對
for split, dataset in drug_dataset_clean.items():
dataset.to_json(f"drug-reviews-{split}.jsonl")這將每個分割儲存為JSON Lines 格式,其中資料集中的每一行都儲存為一行 JSON。以下是第一個示例的外觀
!head -n 1 drug-reviews-train.jsonl{"patient_id":141780,"drugName":"Escitalopram","condition":"depression","review":"\"I seemed to experience the regular side effects of LEXAPRO, insomnia, low sex drive, sleepiness during the day. I am taking it at night because my doctor said if it made me tired to take it at night. I assumed it would and started out taking it at night. Strange dreams, some pleasant. I was diagnosed with fibromyalgia. Seems to be helping with the pain. Have had anxiety and depression in my family, and have tried quite a few other medications that haven't worked. Only have been on it for two weeks but feel more positive in my mind, want to accomplish more in my life. Hopefully the side effects will dwindle away, worth it to stick with it from hearing others responses. Great medication.\"","rating":9.0,"date":"May 29, 2011","usefulCount":10,"review_length":125}然後,我們可以使用第 2 節中的技術載入 JSON 檔案,如下所示
data_files = {
"train": "drug-reviews-train.jsonl",
"validation": "drug-reviews-validation.jsonl",
"test": "drug-reviews-test.jsonl",
}
drug_dataset_reloaded = load_dataset("json", data_files=data_files)這就是我們使用 🤗 Datasets 進行資料整理的旅程的全部內容!現在我們已經為訓練模型準備了一個清理後的資料集,以下是一些你可以嘗試的想法
接下來,我們將瞭解 🤗 Datasets 如何幫助你在不佔用筆記型電腦的情況下處理海量資料集!