r"""undocumented"""
__all__ = [
"CLSBaseLoader",
"YelpFullLoader",
"YelpPolarityLoader",
"AGsNewsLoader",
"DBPediaLoader",
"IMDBLoader",
"SSTLoader",
"SST2Loader",
"ChnSentiCorpLoader",
"THUCNewsLoader",
"WeiboSenti100kLoader",
"MRLoader",
"R8Loader",
"R52Loader",
"OhsumedLoader",
"NG20Loader",
]
import glob
import os
import random
import shutil
import time
import warnings
from .loader import Loader
from ...core.dataset import DataSet
from ...core.instance import Instance
from ...core._logger import logger
[文档]class CLSBaseLoader(Loader):
r"""
文本分类Loader的一个基类
原始数据中内容应该为, 每一行为一个sample,第一个逗号之前为target,第一个逗号之后为文本内容。
Example::
"1","I got 'new' tires from the..."
"1","Don't waste your time..."
读取的DataSet将具备以下的数据结构
.. csv-table::
:header: "raw_words", "target"
"I got 'new' tires from them and... ", "1"
"Don't waste your time. We had two...", "1"
"...", "..."
"""
def __init__(self, sep=',', has_header=False):
super().__init__()
self.sep = sep
self.has_header = has_header
def _load(self, path: str):
ds = DataSet()
try:
with open(path, 'r', encoding='utf-8') as f:
read_header = self.has_header
for line in f:
if read_header:
read_header = False
continue
line = line.strip()
sep_index = line.index(self.sep)
target = line[:sep_index]
raw_words = line[sep_index + 1:]
if target.startswith("\""):
target = target[1:]
if target.endswith("\""):
target = target[:-1]
if raw_words.endswith("\""):
raw_words = raw_words[:-1]
if raw_words.startswith('"'):
raw_words = raw_words[1:]
raw_words = raw_words.replace('""', '"') # 替换双引号
if raw_words:
ds.append(Instance(raw_words=raw_words, target=target))
except Exception as e:
logger.error(f'Load file `{path}` failed for `{e}`')
return ds
def _split_dev(dataset_name, data_dir, dev_ratio=0.0, re_download=False, suffix='csv'):
if dev_ratio == 0.0:
return data_dir
modify_time = 0
for filepath in glob.glob(os.path.join(data_dir, '*')):
modify_time = os.stat(filepath).st_mtime
break
if time.time() - modify_time > 1 and re_download: # 通过这种比较丑陋的方式判断一下文件是否是才下载的
shutil.rmtree(data_dir)
data_dir = Loader()._get_dataset_path(dataset_name=dataset_name)
if not os.path.exists(os.path.join(data_dir, f'dev.{suffix}')):
if dev_ratio > 0:
assert 0 < dev_ratio < 1, "dev_ratio should be in range (0,1)."
try:
with open(os.path.join(data_dir, f'train.{suffix}'), 'r', encoding='utf-8') as f, \
open(os.path.join(data_dir, f'middle_file.{suffix}'), 'w', encoding='utf-8') as f1, \
open(os.path.join(data_dir, f'dev.{suffix}'), 'w', encoding='utf-8') as f2:
for line in f:
if random.random() < dev_ratio:
f2.write(line)
else:
f1.write(line)
os.remove(os.path.join(data_dir, f'train.{suffix}'))
os.renames(os.path.join(data_dir, f'middle_file.{suffix}'), os.path.join(data_dir, f'train.{suffix}'))
finally:
if os.path.exists(os.path.join(data_dir, f'middle_file.{suffix}')):
os.remove(os.path.join(data_dir, f'middle_file.{suffix}'))
return data_dir
class AGsNewsLoader(CLSBaseLoader):
def download(self):
r"""
自动下载数据集,如果你使用了这个数据集,请引用以下的文章
Xiang Zhang, Junbo Zhao, Yann LeCun. Character-level Convolutional Networks for Text Classification. Advances
in Neural Information Processing Systems 28 (NIPS 2015)
:return: str, 数据集的目录地址
"""
return self._get_dataset_path(dataset_name='ag-news')
class DBPediaLoader(CLSBaseLoader):
def download(self, dev_ratio: float = 0.0, re_download: bool = False):
r"""
自动下载数据集,如果你使用了这个数据集,请引用以下的文章
Xiang Zhang, Junbo Zhao, Yann LeCun. Character-level Convolutional Networks for Text Classification. Advances
in Neural Information Processing Systems 28 (NIPS 2015)
如果dev_ratio不等于0,则根据dev_ratio的值随机将train中的数据取出一部分作为dev数据。
下载完成后在output_dir中有train.csv, test.csv, dev.csv三个文件。否则只有train.csv和test.csv
:param float dev_ratio: 如果路径中没有dev集,从train划分多少作为dev的数据. 如果为0,则不划分dev。
:param bool re_download: 是否重新下载数据,以重新切分数据。
:return: str, 数据集的目录地址
"""
dataset_name = 'dbpedia'
data_dir = self._get_dataset_path(dataset_name=dataset_name)
data_dir = _split_dev(dataset_name=dataset_name,
data_dir=data_dir,
dev_ratio=dev_ratio,
re_download=re_download,
suffix='csv')
return data_dir
[文档]class IMDBLoader(CLSBaseLoader):
r"""
原始数据中内容应该为, 每一行为一个sample,制表符之前为target,制表符之后为文本内容。
Example::
neg Alan Rickman & Emma...
neg I have seen this...
IMDBLoader读取后的数据将具有以下两列内容: raw_words: str, 需要分类的文本; target: str, 文本的标签
读取的DataSet具备以下的结构:
.. csv-table::
:header: "raw_words", "target"
"Alan Rickman & Emma... ", "neg"
"I have seen this... ", "neg"
"...", "..."
"""
def __init__(self):
super().__init__(sep='\t')
[文档] def download(self, dev_ratio: float = 0.0, re_download=False):
r"""
自动下载数据集,如果你使用了这个数据集,请引用以下的文章
http://www.aclweb.org/anthology/P11-1015
根据dev_ratio的值随机将train中的数据取出一部分作为dev数据。下载完成后不从train中切分dev
:param float dev_ratio: 如果路径中没有dev.txt。从train划分多少作为dev的数据. 如果为0,则不划分dev
:param bool re_download: 是否重新下载数据,以重新切分数据。
:return: str, 数据集的目录地址
"""
dataset_name = 'aclImdb'
data_dir = self._get_dataset_path(dataset_name=dataset_name)
data_dir = _split_dev(dataset_name=dataset_name,
data_dir=data_dir,
dev_ratio=dev_ratio,
re_download=re_download,
suffix='txt')
return data_dir
[文档]class SSTLoader(Loader):
r"""
原始数据中内容应该为:
Example::
(2 (3 (3 Effective) (2 but)) (1 (1 too-tepid)...
(3 (3 (2 If) (3 (2 you) (3 (2 sometimes)...
读取之后的DataSet具有以下的结构
.. csv-table:: 下面是使用SSTLoader读取的DataSet所具备的field
:header: "raw_words"
"(2 (3 (3 Effective) (2 but)) (1 (1 too-tepid)..."
"(3 (3 (2 If) (3 (2 you) (3 (2 sometimes) ..."
"..."
raw_words列是str。
"""
def __init__(self):
super().__init__()
def _load(self, path: str):
r"""
从path读取SST文件
:param str path: 文件路径
:return: DataSet
"""
ds = DataSet()
with open(path, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if line:
ds.append(Instance(raw_words=line))
return ds
[文档] def download(self):
r"""
自动下载数据集,如果你使用了这个数据集,请引用以下的文章
https://nlp.stanford.edu/~socherr/EMNLP2013_RNTN.pdf
:return: str, 数据集的目录地址
"""
output_dir = self._get_dataset_path(dataset_name='sst')
return output_dir
class YelpFullLoader(CLSBaseLoader):
def download(self, dev_ratio: float = 0.0, re_download: bool = False):
r"""
自动下载数据集,如果你使用了这个数据集,请引用以下的文章
Xiang Zhang, Junbo Zhao, Yann LeCun. Character-level Convolutional Networks for Text Classification. Advances
in Neural Information Processing Systems 28 (NIPS 2015)
如果dev_ratio不等于0,则根据dev_ratio的值随机将train中的数据取出一部分作为dev数据。
下载完成后在output_dir中有train.csv, test.csv, dev.csv三个文件。否则只有train.csv和test.csv
:param float dev_ratio: 如果路径中没有dev集,从train划分多少作为dev的数据. 如果为0,则不划分dev。
:param bool re_download: 是否重新下载数据,以重新切分数据。
:return: str, 数据集的目录地址
"""
dataset_name = 'yelp-review-full'
data_dir = self._get_dataset_path(dataset_name=dataset_name)
data_dir = _split_dev(dataset_name=dataset_name,
data_dir=data_dir,
dev_ratio=dev_ratio,
re_download=re_download,
suffix='csv')
return data_dir
class YelpPolarityLoader(CLSBaseLoader):
def download(self, dev_ratio: float = 0.0, re_download: bool = False):
r"""
自动下载数据集,如果你使用了这个数据集,请引用以下的文章
Xiang Zhang, Junbo Zhao, Yann LeCun. Character-level Convolutional Networks for Text Classification. Advances
in Neural Information Processing Systems 28 (NIPS 2015)
如果dev_ratio不等于0,则根据dev_ratio的值随机将train中的数据取出一部分作为dev数据。
下载完成后在output_dir中有train.csv, test.csv, dev.csv三个文件。否则只有train.csv和test.csv
:param float dev_ratio: 如果路径中没有dev集,从train划分多少作为dev的数据. 如果为0,则不划分dev。
:param bool re_download: 是否重新下载数据,以重新切分数据。
:return: str, 数据集的目录地址
"""
dataset_name = 'yelp-review-polarity'
data_dir = self._get_dataset_path(dataset_name=dataset_name)
data_dir = _split_dev(dataset_name=dataset_name,
data_dir=data_dir,
dev_ratio=dev_ratio,
re_download=re_download,
suffix='csv')
return data_dir
[文档]class SST2Loader(Loader):
r"""
原始数据中内容为:第一行为标题(具体内容会被忽略),之后一行为一个sample,第一个制表符之前被认为是句子,第一个制表符之后认为是label
Example::
sentence label
it 's a charming and often affecting journey . 1
unflinchingly bleak and desperate 0
读取之后DataSet将如下所示
.. csv-table::
:header: "raw_words", "target"
"it 's a charming and often affecting journey .", "1"
"unflinchingly bleak and desperate", "0"
"..."
test的DataSet没有target列。
"""
def __init__(self):
super().__init__()
def _load(self, path: str):
r"""从path读取SST2文件
:param str path: 数据路径
:return: DataSet
"""
ds = DataSet()
with open(path, 'r', encoding='utf-8') as f:
f.readline() # 跳过header
if 'test' in os.path.split(path)[1]:
warnings.warn("SST2's test file has no target.")
for line in f:
line = line.strip()
if line:
sep_index = line.index('\t')
raw_words = line[sep_index + 1:]
index = int(line[: sep_index])
if raw_words:
ds.append(Instance(raw_words=raw_words, index=index))
else:
for line in f:
line = line.strip()
if line:
raw_words = line[:-2]
target = line[-1]
if raw_words:
ds.append(Instance(raw_words=raw_words, target=target))
return ds
[文档] def download(self):
r"""
自动下载数据集,如果你使用了该数据集,请引用以下的文章
https://nlp.stanford.edu/pubs/SocherBauerManningNg_ACL2013.pdf
:return:
"""
output_dir = self._get_dataset_path(dataset_name='sst-2')
return output_dir
[文档]class ChnSentiCorpLoader(Loader):
r"""
支持读取的数据的格式为,第一行为标题(具体内容会被忽略),之后一行为一个sample,第一个制表符之前被认为是label,第
一个制表符之后认为是句子
Example::
label text_a
1 基金痛所有投资项目一样,必须先要有所了解...
1 系统很好装,LED屏是不错,就是16比9的比例...
读取后的DataSet具有以下的field
.. csv-table::
:header: "raw_chars", "target"
"基金痛所有投资项目一样,必须先要有所了解...", "1"
"系统很好装,LED屏是不错,就是16比9的比例...", "1"
"..."
"""
def __init__(self):
super().__init__()
def _load(self, path: str):
r"""
从path中读取数据
:param path:
:return:
"""
ds = DataSet()
with open(path, 'r', encoding='utf-8') as f:
f.readline()
for line in f:
line = line.strip()
tab_index = line.index('\t')
if tab_index != -1:
target = line[:tab_index]
raw_chars = line[tab_index + 1:]
if raw_chars:
ds.append(Instance(raw_chars=raw_chars, target=target))
return ds
[文档] def download(self) -> str:
r"""
自动下载数据,该数据取自https://github.com/pengming617/bert_classification/tree/master/data,在
https://arxiv.org/pdf/1904.09223.pdf与https://arxiv.org/pdf/1906.08101.pdf有使用
:return:
"""
output_dir = self._get_dataset_path('chn-senti-corp')
return output_dir
[文档]class THUCNewsLoader(Loader):
r"""
数据集简介:document-level分类任务,新闻10分类
原始数据内容为:每行一个sample,第一个 "\\t" 之前为target,第一个 "\\t" 之后为raw_words
Example::
体育 调查-您如何评价热火客场胜绿军总分3-1夺赛点?...
读取后的Dataset将具有以下数据结构:
.. csv-table::
:header: "raw_words", "target"
"调查-您如何评价热火客场胜绿军总分3-1夺赛点?...", "体育"
"...", "..."
"""
def __init__(self):
super(THUCNewsLoader, self).__init__()
def _load(self, path: str = None):
ds = DataSet()
with open(path, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
sep_index = line.index('\t')
raw_chars = line[sep_index + 1:]
target = line[:sep_index]
if raw_chars:
ds.append(Instance(raw_chars=raw_chars, target=target))
return ds
[文档] def download(self) -> str:
r"""
自动下载数据,该数据取自
http://thuctc.thunlp.org/#%E4%B8%AD%E6%96%87%E6%96%87%E6%9C%AC%E5%88%86%E7%B1%BB%E6%95%B0%E6%8D%AE%E9%9B%86THUCNews
:return:
"""
output_dir = self._get_dataset_path('thuc-news')
return output_dir
[文档]class WeiboSenti100kLoader(Loader):
r"""
别名:
数据集简介:微博sentiment classification,二分类
Example::
label text
1 多谢小莲,好运满满[爱你]
1 能在他乡遇老友真不赖,哈哈,珠儿,我也要用...
读取后的Dataset将具有以下数据结构:
.. csv-table::
:header: "raw_chars", "target"
"多谢小莲,好运满满[爱你]", "1"
"能在他乡遇老友真不赖,哈哈,珠儿,我也要用...", "1"
"...", "..."
"""
def __init__(self):
super(WeiboSenti100kLoader, self).__init__()
def _load(self, path: str = None):
ds = DataSet()
with open(path, 'r', encoding='utf-8') as f:
next(f)
for line in f:
line = line.strip()
target = line[0]
raw_chars = line[1:]
if raw_chars:
ds.append(Instance(raw_chars=raw_chars, target=target))
return ds
[文档] def download(self) -> str:
r"""
自动下载数据,该数据取自 https://github.com/SophonPlus/ChineseNlpCorpus/
在 https://arxiv.org/abs/1906.08101 有使用
:return:
"""
output_dir = self._get_dataset_path('weibo-senti-100k')
return output_dir
class MRLoader(CLSBaseLoader):
def __init__(self):
super(MRLoader, self).__init__()
def download(self, dev_ratio: float = 0.0, re_download: bool = False) -> str:
r"""
自动下载数据集
如果dev_ratio不等于0,则根据dev_ratio的值随机将train中的数据取出一部分作为dev数据。
下载完成后在output_dir中有train.csv, test.csv, dev.csv三个文件。否则只有train.csv和test.csv
:param float dev_ratio: 如果路径中没有dev集,从train划分多少作为dev的数据. 如果为0,则不划分dev。
:param bool re_download: 是否重新下载数据,以重新切分数据。
:return: str, 数据集的目录地址
"""
dataset_name = r'mr'
data_dir = self._get_dataset_path(dataset_name=dataset_name)
data_dir = _split_dev(dataset_name=dataset_name,
data_dir=data_dir,
dev_ratio=dev_ratio,
re_download=re_download,
suffix='csv')
return data_dir
class R8Loader(CLSBaseLoader):
def __init__(self):
super(R8Loader, self).__init__()
def download(self, dev_ratio: float = 0.0, re_download: bool = False) -> str:
r"""
自动下载数据集
如果dev_ratio不等于0,则根据dev_ratio的值随机将train中的数据取出一部分作为dev数据。
下载完成后在output_dir中有train.csv, test.csv, dev.csv三个文件。否则只有train.csv和test.csv
:param float dev_ratio: 如果路径中没有dev集,从train划分多少作为dev的数据. 如果为0,则不划分dev。
:param bool re_download: 是否重新下载数据,以重新切分数据。
:return: str, 数据集的目录地址
"""
dataset_name = r'R8'
data_dir = self._get_dataset_path(dataset_name=dataset_name)
data_dir = _split_dev(dataset_name=dataset_name,
data_dir=data_dir,
dev_ratio=dev_ratio,
re_download=re_download,
suffix='csv')
return data_dir
class R52Loader(CLSBaseLoader):
def __init__(self):
super(R52Loader, self).__init__()
def download(self, dev_ratio: float = 0.0, re_download: bool = False) -> str:
r"""
自动下载数据集
如果dev_ratio不等于0,则根据dev_ratio的值随机将train中的数据取出一部分作为dev数据。
下载完成后在output_dir中有train.csv, test.csv, dev.csv三个文件。否则只有train.csv和test.csv
:param float dev_ratio: 如果路径中没有dev集,从train划分多少作为dev的数据. 如果为0,则不划分dev。
:param bool re_download: 是否重新下载数据,以重新切分数据。
:return: str, 数据集的目录地址
"""
dataset_name = r'R52'
data_dir = self._get_dataset_path(dataset_name=dataset_name)
data_dir = _split_dev(dataset_name=dataset_name,
data_dir=data_dir,
dev_ratio=dev_ratio,
re_download=re_download,
suffix='csv')
return data_dir
class NG20Loader(CLSBaseLoader):
def __init__(self):
super(NG20Loader, self).__init__()
def download(self, dev_ratio: float = 0.0, re_download: bool = False) -> str:
r"""
自动下载数据集
如果dev_ratio不等于0,则根据dev_ratio的值随机将train中的数据取出一部分作为dev数据。
下载完成后在output_dir中有train.csv, test.csv, dev.csv三个文件。否则只有train.csv和test.csv
:param float dev_ratio: 如果路径中没有dev集,从train划分多少作为dev的数据. 如果为0,则不划分dev。
:param bool re_download: 是否重新下载数据,以重新切分数据。
:return: str, 数据集的目录地址
"""
dataset_name = r'20ng'
data_dir = self._get_dataset_path(dataset_name=dataset_name)
data_dir = _split_dev(dataset_name=dataset_name,
data_dir=data_dir,
dev_ratio=dev_ratio,
re_download=re_download,
suffix='csv')
return data_dir
class OhsumedLoader(CLSBaseLoader):
def __init__(self):
super(OhsumedLoader, self).__init__()
def download(self, dev_ratio: float = 0.0, re_download: bool = False) -> str:
r"""
自动下载数据集
如果dev_ratio不等于0,则根据dev_ratio的值随机将train中的数据取出一部分作为dev数据。
下载完成后在output_dir中有train.csv, test.csv, dev.csv三个文件。否则只有train.csv和test.csv
:param float dev_ratio: 如果路径中没有dev集,从train划分多少作为dev的数据. 如果为0,则不划分dev。
:param bool re_download: 是否重新下载数据,以重新切分数据。
:return: str, 数据集的目录地址
"""
dataset_name = r'ohsumed'
data_dir = self._get_dataset_path(dataset_name=dataset_name)
data_dir = _split_dev(dataset_name=dataset_name,
data_dir=data_dir,
dev_ratio=dev_ratio,
re_download=re_download,
suffix='csv')
return data_dir