Hãy làm theo hướng dẫn này để tạo bộ dữ liệu mới (trong TFDS hoặc trong kho lưu trữ của riêng bạn).
Hãy kiểm tra danh sách các bộ dữ liệu của chúng tôi để xem bộ dữ liệu bạn cần đã có sẵn hay chưa.
Tóm lại
Cách dễ nhất để tạo một tập dữ liệu mới là sử dụng giao diện dòng lệnh TFDS :
cd path/to/my/project/datasets/
tfds new my_dataset # Create `my_dataset/my_dataset.py` template files
# [...] Manually modify `my_dataset/my_dataset_dataset_builder.py` to implement your dataset.
cd my_dataset/
tfds build # Download and prepare the dataset to `~/tensorflow_datasets/`
Để sử dụng tập dữ liệu mới với tfds.load('my_dataset') :
-
tfds.loadsẽ tự động phát hiện và tải tập dữ liệu được tạo trong~/tensorflow_datasets/my_dataset/(ví dụ: bởitfds build). - Ngoài ra, bạn cũng có thể
import my.project.datasets.my_datasetđể đăng ký tập dữ liệu của mình:
import my.project.datasets.my_dataset # Register `my_dataset`
ds = tfds.load('my_dataset') # `my_dataset` registered
Tổng quan
Các tập dữ liệu được phân phối dưới nhiều định dạng và ở nhiều địa điểm khác nhau, và không phải lúc nào chúng cũng được lưu trữ ở định dạng sẵn sàng để đưa vào quy trình học máy. Đó là lý do TFDS ra đời.
TFDS xử lý các tập dữ liệu đó thành một định dạng chuẩn (dữ liệu bên ngoài -> tệp được tuần tự hóa), sau đó có thể được tải vào quy trình xử lý dữ liệu máy học (tệp được tuần tự hóa -> tf.data.Dataset ). Quá trình tuần tự hóa chỉ được thực hiện một lần. Các lần truy cập tiếp theo sẽ đọc trực tiếp từ các tệp đã được xử lý trước đó.
Hầu hết các bước tiền xử lý được thực hiện tự động. Mỗi tập dữ liệu triển khai một lớp con của tfds.core.DatasetBuilder , trong đó quy định:
- Nguồn gốc của dữ liệu (tức là URL của dữ liệu);
- Dữ liệu có cấu trúc như thế nào (tức là các đặc điểm của nó);
- Nên chia dữ liệu như thế nào (ví dụ: dữ
TRAINvàTEST); - và các ví dụ riêng lẻ trong tập dữ liệu.
Hãy viết bộ dữ liệu của bạn
Mẫu mặc định: tfds new
Sử dụng TFDS CLI để tạo các tệp Python mẫu cần thiết.
cd path/to/project/datasets/ # Or use `--dir=path/to/project/datasets/` below
tfds new my_dataset
Lệnh này sẽ tạo một thư mục mới my_dataset/ với cấu trúc như sau:
my_dataset/
__init__.py
README.md # Markdown description of the dataset.
CITATIONS.bib # Bibtex citation for the dataset.
TAGS.txt # List of tags describing the dataset.
my_dataset_dataset_builder.py # Dataset definition
my_dataset_dataset_builder_test.py # Test
dummy_data/ # (optional) Fake data (used for testing)
checksum.tsv # (optional) URL checksums (see `checksums` section).
Tìm kiếm TODO(my_dataset) ở đây và chỉnh sửa cho phù hợp.
Ví dụ về tập dữ liệu
Tất cả các tập dữ liệu đều được triển khai dưới dạng các lớp con của tfds.core.DatasetBuilder , lớp này đảm nhiệm hầu hết các công việc lặp lại. Nó hỗ trợ:
- Các tập dữ liệu nhỏ/trung bình có thể được tạo trên một máy tính duy nhất (hướng dẫn này).
- Các tập dữ liệu rất lớn yêu cầu tạo dữ liệu phân tán (sử dụng Apache Beam , xem hướng dẫn về tập dữ liệu khổng lồ của chúng tôi).
Dưới đây là một ví dụ tối thiểu về trình tạo tập dữ liệu dựa trên tfds.core.GeneratorBasedBuilder :
class Builder(tfds.core.GeneratorBasedBuilder):
"""DatasetBuilder for my_dataset dataset."""
VERSION = tfds.core.Version('1.0.0')
RELEASE_NOTES = {
'1.0.0': 'Initial release.',
}
def _info(self) -> tfds.core.DatasetInfo:
"""Dataset metadata (homepage, citation,...)."""
return self.dataset_info_from_configs(
features=tfds.features.FeaturesDict({
'image': tfds.features.Image(shape=(256, 256, 3)),
'label': tfds.features.ClassLabel(
names=['no', 'yes'],
doc='Whether this is a picture of a cat'),
}),
)
def _split_generators(self, dl_manager: tfds.download.DownloadManager):
"""Download the data and define splits."""
extracted_path = dl_manager.download_and_extract('http://data.org/data.zip')
# dl_manager returns pathlib-like objects with `path.read_text()`,
# `path.iterdir()`,...
return {
'train': self._generate_examples(path=extracted_path / 'train_images'),
'test': self._generate_examples(path=extracted_path / 'test_images'),
}
def _generate_examples(self, path) -> Iterator[Tuple[Key, Example]]:
"""Generator of examples for each split."""
for img_path in path.glob('*.jpeg'):
# Yields (key, example)
yield img_path.name, {
'image': img_path,
'label': 'yes' if img_path.name.startswith('yes_') else 'no',
}
Xin lưu ý rằng, đối với một số định dạng dữ liệu cụ thể, chúng tôi cung cấp các công cụ tạo tập dữ liệu sẵn sàng sử dụng để xử lý hầu hết các công đoạn xử lý dữ liệu.
Chúng ta hãy cùng xem chi tiết 3 phương pháp trừu tượng để ghi đè.
_info : siêu dữ liệu tập dữ liệu
_info trả về tfds.core.DatasetInfo chứa siêu dữ liệu của tập dữ liệu .
def _info(self):
# The `dataset_info_from_configs` base method will construct the
# `tfds.core.DatasetInfo` object using the passed-in parameters and
# adding: builder (self), description/citations/tags from the config
# files located in the same package.
return self.dataset_info_from_configs(
homepage='https://dataset-homepage.org',
features=tfds.features.FeaturesDict({
'image_description': tfds.features.Text(),
'image': tfds.features.Image(),
# Here, 'label' can be 0-4.
'label': tfds.features.ClassLabel(num_classes=5),
}),
# If there's a common `(input, target)` tuple from the features,
# specify them here. They'll be used if as_supervised=True in
# builder.as_dataset.
supervised_keys=('image', 'label'),
# Specify whether to disable shuffling on the examples. Set to False by default.
disable_shuffling=False,
)
Hầu hết các trường thông tin đều khá dễ hiểu. Một số điểm cần lưu ý:
-
features: Tính năng này xác định cấu trúc, hình dạng của tập dữ liệu,... Hỗ trợ các loại dữ liệu phức tạp (âm thanh, video, chuỗi lồng nhau,...). Xem các tính năng có sẵn hoặc hướng dẫn kết nối tính năng để biết thêm thông tin. -
disable_shuffling: Xem phần Duy trì thứ tự tập dữ liệu .
Ghi file BibText CITATIONS.bib :
- Hãy tìm hướng dẫn trích dẫn trên trang web của bộ dữ liệu (sử dụng định dạng BibTex).
- Đối với các bài báo trên arXiv : hãy tìm bài báo và nhấp vào liên kết
BibTextở phía bên phải. - Tìm bài báo trên Google Scholar và nhấp vào dấu ngoặc kép bên dưới tiêu đề, sau đó trên cửa sổ bật lên, nhấp vào
BibTeX. - Nếu không có bài báo liên quan (ví dụ: chỉ có trang web), bạn có thể sử dụng Trình chỉnh sửa trực tuyến BibTeX để tạo mục BibTeX tùy chỉnh (menu thả xuống có loại mục Trực
Online).
Cập nhật tệp TAGS.txt :
- Tất cả các thẻ được cho phép đều được điền sẵn trong tệp được tạo.
- Xóa bỏ tất cả các thẻ không áp dụng cho tập dữ liệu.
- Các thẻ hợp lệ được liệt kê trong tensorflow_datasets/core/valid_tags.txt .
- Để thêm thẻ vào danh sách đó, vui lòng gửi yêu cầu kéo (PR).
Duy trì thứ tự tập dữ liệu
Theo mặc định, các bản ghi của tập dữ liệu được xáo trộn khi lưu trữ để phân bố các lớp đồng đều hơn trong tập dữ liệu, vì thường các bản ghi thuộc cùng một lớp nằm liền kề nhau. Để chỉ định rằng tập dữ liệu nên được sắp xếp theo khóa được tạo bởi _generate_examples , trường disable_shuffling cần được đặt thành True . Theo mặc định, nó được đặt thành False .
def _info(self):
return self.dataset_info_from_configs(
# [...]
disable_shuffling=True,
# [...]
)
Cần lưu ý rằng việc vô hiệu hóa tính năng xáo trộn sẽ ảnh hưởng đến hiệu năng vì các phân vùng dữ liệu không thể được đọc song song nữa.
_split_generators : tải xuống và chia dữ liệu
Tải xuống và trích xuất dữ liệu nguồn
Hầu hết các tập dữ liệu cần tải dữ liệu từ web. Việc này được thực hiện bằng cách sử dụng đối số đầu vào tfds.download.DownloadManager của _split_generators . dl_manager có các phương thức sau:
-
download: hỗ trợhttp(s)://,ftp(s):// -
extract: hiện hỗ trợ các tệp.zip,.gzvà.tar. -
download_and_extract: Tương tự nhưdl_manager.extract(dl_manager.download(urls))
Tất cả các phương thức đó đều trả về tfds.core.Path (bí danh cho epath.Path ), là các đối tượng giống như pathlib.Path .
Các phương pháp này hỗ trợ cấu trúc lồng nhau tùy ý ( list , dict ), ví dụ:
extracted_paths = dl_manager.download_and_extract({
'foo': 'https://example.com/foo.zip',
'bar': 'https://example.com/bar.zip',
})
# This returns:
assert extracted_paths == {
'foo': Path('/path/to/extracted_foo/'),
'bar': Path('/path/extracted_bar/'),
}
Tải xuống và giải nén thủ công
Một số dữ liệu không thể tự động tải xuống (ví dụ: yêu cầu đăng nhập), trong trường hợp này, người dùng sẽ tải xuống dữ liệu nguồn theo cách thủ công và đặt nó vào manual_dir/ (mặc định là ~/tensorflow_datasets/downloads/manual/ ).
Sau đó, các tệp có thể được truy cập thông qua dl_manager.manual_dir :
class MyDataset(tfds.core.GeneratorBasedBuilder):
MANUAL_DOWNLOAD_INSTRUCTIONS = """
Register into https://example.org/login to get the data. Place the `data.zip`
file in the `manual_dir/`.
"""
def _split_generators(self, dl_manager):
# data_path is a pathlib-like `Path('<manual_dir>/data.zip')`
archive_path = dl_manager.manual_dir / 'data.zip'
# Extract the manually downloaded `data.zip`
extracted_path = dl_manager.extract(archive_path)
...
Vị trí manual_dir có thể được tùy chỉnh bằng tfds build --manual_dir= hoặc sử dụng tfds.download.DownloadConfig .
Đọc trực tiếp từ kho lưu trữ
dl_manager.iter_archive đọc các tệp lưu trữ theo trình tự mà không cần giải nén. Điều này có thể tiết kiệm không gian lưu trữ và cải thiện hiệu suất trên một số hệ thống tệp.
for filename, fobj in dl_manager.iter_archive('path/to/archive.zip'):
...
fobj có các phương thức tương tự như with open('rb') as fobj: (ví dụ: fobj.read() )
Xác định các phần chia của tập dữ liệu
Nếu tập dữ liệu có các phân vùng được xác định trước (ví dụ: MNIST có các phân vùng train và test ), hãy giữ nguyên các phân vùng đó. Nếu không, chỉ cần chỉ định một phân vùng " all " duy nhất. Người dùng có thể tạo các phân vùng con riêng của họ một cách động bằng API phân vùng con (ví dụ: split='train[80%:]' ). Lưu ý rằng bất kỳ chuỗi ký tự nào cũng có thể được sử dụng làm tên phân vùng, ngoại trừ " all đã đề cập ở trên.
def _split_generators(self, dl_manager):
# Download source data
extracted_path = dl_manager.download_and_extract(...)
# Specify the splits
return {
'train': self._generate_examples(
images_path=extracted_path / 'train_imgs',
label_path=extracted_path / 'train_labels.csv',
),
'test': self._generate_examples(
images_path=extracted_path / 'test_imgs',
label_path=extracted_path / 'test_labels.csv',
),
}
_generate_examples : Trình tạo ví dụ
_generate_examples tạo ra các ví dụ cho mỗi lần chia dữ liệu từ dữ liệu nguồn.
Phương pháp này thường sẽ đọc các thành phần dữ liệu nguồn (ví dụ: tệp CSV) và trả về các bộ giá trị (key, feature_dict) :
-
key: Mã định danh ví dụ. Được sử dụng để xáo trộn các ví dụ một cách xác định bằng cách sử dụnghash(key)hoặc để sắp xếp theo key khi việc xáo trộn bị vô hiệu hóa (xem phần Duy trì thứ tự tập dữ liệu ). Nên là:- unique : Nếu hai ví dụ sử dụng cùng một khóa, một ngoại lệ sẽ được đưa ra.
- Mang tính xác định : Không nên phụ thuộc vào
download_dir,os.path.listdirorder,... Việc tạo dữ liệu hai lần sẽ cho ra cùng một khóa. - Tương đương : Nếu chức năng xáo trộn bị vô hiệu hóa, khóa sẽ được sử dụng để sắp xếp tập dữ liệu.
-
feature_dict: Mộtdictchứa các giá trị ví dụ.- Cấu trúc phải khớp với cấu trúc
features=được định nghĩa trongtfds.core.DatasetInfo. - Các loại dữ liệu phức tạp (hình ảnh, video, âm thanh,...) sẽ được mã hóa tự động.
- Mỗi tính năng thường chấp nhận nhiều loại đầu vào (ví dụ: video chấp nhận
/path/to/vid.mp4,np.array(shape=(l, h, w, c)),List[paths],List[np.array(shape=(h, w, c)],List[img_bytes],...) - Xem hướng dẫn kết nối tính năng để biết thêm thông tin.
- Cấu trúc phải khớp với cấu trúc
def _generate_examples(self, images_path, label_path):
# Read the input data out of the source files
with label_path.open() as f:
for row in csv.DictReader(f):
image_id = row['image_id']
# And yield (key, feature_dict)
yield image_id, {
'image_description': row['description'],
'image': images_path / f'{image_id}.jpeg',
'label': row['label'],
}
Truy cập tập tin và tf.io.gfile
Để hỗ trợ các hệ thống lưu trữ đám mây, hãy tránh sử dụng các thao tác I/O tích hợp sẵn của Python.
Thay vào đó, dl_manager trả về các đối tượng giống pathlib, hoàn toàn tương thích với Google Cloud Storage:
path = dl_manager.download_and_extract('http://some-website/my_data.zip')
json_path = path / 'data/file.json'
json.loads(json_path.read_text())
Ngoài ra, bạn cũng có thể sử dụng API tf.io.gfile thay vì API tích hợp sẵn để thực hiện các thao tác với tệp:
-
open->tf.io.gfile.GFile -
os.rename->tf.io.gfile.rename - ...
Nên ưu tiên sử dụng Pathlib thay vì tf.io.gfile (xem rational .
Các phụ thuộc bổ sung
Một số tập dữ liệu chỉ yêu cầu các thư viện Python bổ sung trong quá trình tạo. Ví dụ, tập dữ liệu SVHN sử dụng scipy để tải một số dữ liệu.
Nếu bạn đang thêm tập dữ liệu vào kho lưu trữ TFDS, vui lòng sử dụng tfds.core.lazy_imports để giữ cho gói tensorflow-datasets có kích thước nhỏ. Người dùng sẽ chỉ cài đặt các phụ thuộc bổ sung khi cần thiết.
Để sử dụng lazy_imports :
- Thêm mục nhập cho tập dữ liệu của bạn vào
DATASET_EXTRAStrongsetup.py. Điều này cho phép người dùng thực hiện, ví dụ,pip install 'tensorflow-datasets[svhn]'để cài đặt các phụ thuộc bổ sung. - Thêm một mục cho quá trình nhập dữ liệu của bạn vào
LazyImportervà vàoLazyImportsTest. - Sử dụng
tfds.core.lazy_importsđể truy cập vào phần phụ thuộc (ví dụ:tfds.core.lazy_imports.scipy) trongDatasetBuildercủa bạn.
Dữ liệu bị hỏng
Một số tập dữ liệu không hoàn toàn sạch và chứa một số dữ liệu bị lỗi (ví dụ: hình ảnh ở định dạng JPEG nhưng một số là JPEG không hợp lệ). Những ví dụ này nên được bỏ qua, nhưng hãy ghi chú trong phần mô tả tập dữ liệu số lượng ví dụ đã bị loại bỏ và lý do.
Cấu hình/biến thể tập dữ liệu (tfds.core.BuilderConfig)
Một số tập dữ liệu có thể có nhiều biến thể, hoặc nhiều tùy chọn về cách dữ liệu được xử lý trước và ghi vào đĩa. Ví dụ, cycle_gan có một cấu hình cho mỗi cặp đối tượng ( cycle_gan/horse2zebra , cycle_gan/monet2photo ,...).
Việc này được thực hiện thông qua tfds.core.BuilderConfig s:
Hãy định nghĩa đối tượng cấu hình của bạn như một lớp con của
tfds.core.BuilderConfig. Ví dụ:MyDatasetConfig.@dataclasses.dataclass class MyDatasetConfig(tfds.core.BuilderConfig): img_size: Tuple[int, int] = (0, 0)Hãy định nghĩa thành viên lớp
BUILDER_CONFIGS = []trongMyDatasetđể liệt kê cácMyDatasetConfigmà tập dữ liệu đó cung cấp.class MyDataset(tfds.core.GeneratorBasedBuilder): VERSION = tfds.core.Version('1.0.0') # pytype: disable=wrong-keyword-args BUILDER_CONFIGS = [ # `name` (and optionally `description`) are required for each config MyDatasetConfig(name='small', description='Small ...', img_size=(8, 8)), MyDatasetConfig(name='big', description='Big ...', img_size=(32, 32)), ] # pytype: enable=wrong-keyword-argsSử dụng
self.builder_configtrongMyDatasetđể cấu hình việc tạo dữ liệu (ví dụ:shape=self.builder_config.img_size). Điều này có thể bao gồm việc thiết lập các giá trị khác nhau trong_info()hoặc thay đổi quyền truy cập dữ liệu tải xuống.
Ghi chú:
- Mỗi cấu hình có một tên duy nhất. Tên đầy đủ của một cấu hình có
dataset_name/config_name(ví dụ:coco/2017). - Nếu không được chỉ định, cấu hình đầu tiên trong
BUILDER_CONFIGSsẽ được sử dụng (ví dụ:tfds.load('c4')mặc định làc4/en)
Xem anli để biết ví dụ về tập dữ liệu sử dụng BuilderConfig .
Phiên bản
Phiên bản có thể mang hai ý nghĩa khác nhau:
- Phiên bản dữ liệu gốc "bên ngoài": ví dụ: COCO v2019, v2017,...
- Phiên bản mã TFDS "nội bộ": ví dụ: đổi tên một thuộc tính trong
tfds.features.FeaturesDict, sửa lỗi trong_generate_examples
Để cập nhật tập dữ liệu:
- Đối với việc cập nhật dữ liệu "bên ngoài": Nhiều người dùng có thể muốn truy cập đồng thời vào một năm/phiên bản cụ thể. Điều này được thực hiện bằng cách sử dụng một
tfds.core.BuilderConfigcho mỗi phiên bản (ví dụ:coco/2017,coco/2019) hoặc một lớp cho mỗi phiên bản (ví dụ:Voc2007,Voc2012). - Đối với việc cập nhật mã "nội bộ": Người dùng chỉ tải xuống phiên bản mới nhất. Bất kỳ bản cập nhật mã nào cũng nên tăng thuộc tính lớp
VERSION(ví dụ: từ1.0.0thànhVERSION = tfds.core.Version('2.0.0')) theo phiên bản ngữ nghĩa .
Thêm mục nhập khẩu để đăng ký
Đừng quên nhập mô-đun dataset vào phương thức __init__ của dự án để nó được tự động đăng ký trong tfds.load và tfds.builder .
import my_project.datasets.my_dataset # Register MyDataset
ds = tfds.load('my_dataset') # MyDataset available
Ví dụ, nếu bạn đang đóng góp cho tensorflow/datasets , hãy thêm lệnh import mô-đun vào tệp __init__.py trong thư mục con của nó (ví dụ: image/__init__.py ).
Kiểm tra các lỗi thường gặp khi triển khai.
Vui lòng kiểm tra các lỗi thường gặp trong quá trình triển khai .
Kiểm tra tập dữ liệu của bạn
Tải xuống và chuẩn bị: tfds build
Để tạo tập dữ liệu, hãy chạy tfds build từ thư mục my_dataset/ :
cd path/to/datasets/my_dataset/
tfds build --register_checksums
Một số cờ hữu ích cho quá trình phát triển:
-
--pdb: Vào chế độ gỡ lỗi nếu có ngoại lệ xảy ra. -
--overwrite: Xóa các tệp hiện có nếu tập dữ liệu đã được tạo. -
--max_examples_per_split: Chỉ tạo ra X ví dụ đầu tiên (mặc định là 1), thay vì toàn bộ tập dữ liệu. -
--register_checksums: Ghi lại mã kiểm tra của các URL đã tải xuống. Chỉ nên sử dụng trong quá trình phát triển.
Xem tài liệu CLI để biết danh sách đầy đủ các cờ.
Tổng kiểm tra
Nên ghi lại mã kiểm tra (checksum) của các tập dữ liệu để đảm bảo tính xác định, hỗ trợ việc lập tài liệu,... Việc này được thực hiện bằng cách tạo tập dữ liệu với tùy chọn --register_checksums (xem phần trước).
Nếu bạn đang phát hành bộ dữ liệu của mình thông qua PyPI, đừng quên xuất các tệp checksums.tsv (ví dụ: trong thư package_data của tệp setup.py ).
Kiểm thử đơn vị tập dữ liệu của bạn
tfds.testing.DatasetBuilderTestCase là một TestCase cơ bản để kiểm tra đầy đủ một tập dữ liệu. Nó sử dụng "dữ liệu giả" làm dữ liệu thử nghiệm mô phỏng cấu trúc của tập dữ liệu nguồn.
- Dữ liệu thử nghiệm cần được đặt trong thư mục
my_dataset/dummy_data/và phải mô phỏng các thành phần dữ liệu nguồn như khi được tải xuống và giải nén. Có thể tạo dữ liệu này thủ công hoặc tự động bằng một tập lệnh ( ví dụ tập lệnh ). - Hãy đảm bảo sử dụng các tập dữ liệu khác nhau trong các phần dữ liệu thử nghiệm của bạn, vì bài kiểm tra sẽ thất bại nếu các phần dữ liệu được chia chồng chéo lên nhau.
- Dữ liệu thử nghiệm không được chứa bất kỳ tài liệu có bản quyền nào . Nếu nghi ngờ, không nên tạo dữ liệu bằng cách sử dụng tài liệu từ bộ dữ liệu gốc.
import tensorflow_datasets as tfds
from . import my_dataset_dataset_builder
class MyDatasetTest(tfds.testing.DatasetBuilderTestCase):
"""Tests for my_dataset dataset."""
DATASET_CLASS = my_dataset_dataset_builder.Builder
SPLITS = {
'train': 3, # Number of fake train example
'test': 1, # Number of fake test example
}
# If you are calling `download/download_and_extract` with a dict, like:
# dl_manager.download({'some_key': 'http://a.org/out.txt', ...})
# then the tests needs to provide the fake output paths relative to the
# fake data directory
DL_EXTRACT_RESULT = {
'name1': 'path/to/file1', # Relative to my_dataset/dummy_data dir.
'name2': 'file2',
}
if __name__ == '__main__':
tfds.testing.test_main()
Chạy lệnh sau để kiểm tra tập dữ liệu.
python my_dataset_test.py
Hãy gửi phản hồi cho chúng tôi
Chúng tôi liên tục cố gắng cải thiện quy trình tạo tập dữ liệu, nhưng chỉ có thể làm được điều đó nếu chúng tôi nhận thức được các vấn đề. Bạn đã gặp phải vấn đề hoặc lỗi nào trong quá trình tạo tập dữ liệu? Có phần nào gây khó hiểu hoặc không hoạt động ngay lần đầu tiên không?
Vui lòng chia sẻ phản hồi của bạn trên GitHub .