Skip to content

Commit e27ac6a

Browse files
authored
Support segmented inputs for ddparser (#1351)
* Support segmented inputs for ddparser * Update usage * Update ddparser segmode * Update README.md * Update README.md * Update taskflow.md
1 parent 570685f commit e27ac6a

File tree

3 files changed

+111
-42
lines changed

3 files changed

+111
-42
lines changed

docs/model_zoo/taskflow.md

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ corrector(['遇到逆竟时,我们必须勇于面对,而且要愈挫愈勇
207207

208208
### 句法分析
209209

210+
未分词输入:
211+
210212
```python
211213
from paddlenlp import Taskflow
212214

@@ -216,21 +218,33 @@ ddp("9月9日上午纳达尔在亚瑟·阿什球场击败俄罗斯球员梅德
216218

217219
ddp(["9月9日上午纳达尔在亚瑟·阿什球场击败俄罗斯球员梅德韦杰夫", "他送了一本书"])
218220
>>> [{'word': ['9月9日', '上午', '纳达尔', '', '亚瑟·阿什球场', '击败', '俄罗斯', '球员', '梅德韦杰夫'], 'head': [2, 6, 6, 5, 6, 0, 8, 9, 6], 'deprel': ['ATT', 'ADV', 'SBV', 'MT', 'ADV', 'HED', 'ATT', 'ATT', 'VOB']}, {'word': ['', '', '', '一本', ''], 'head': [2, 0, 2, 5, 2], 'deprel': ['SBV', 'HED', 'MT', 'ATT', 'VOB']}]
221+
```
219222

220-
# 输出概率值和词性标签
223+
输出概率值和词性标签:
224+
225+
```python
221226
ddp = Taskflow("dependency_parsing", prob=True, use_pos=True)
222227
ddp("9月9日上午纳达尔在亚瑟·阿什球场击败俄罗斯球员梅德韦杰夫")
223228
>>> [{'word': ['9月9日', '上午', '纳达尔', '', '亚瑟·阿什', '球场', '击败', '俄罗斯', '球员', '梅德韦杰夫'], 'head': [2, 7, 7, 6, 6, 7, 0, 9, 10, 7], 'deprel': ['ATT', 'ADV', 'SBV', 'MT', 'ATT', 'ADV', 'HED', 'ATT', 'ATT', 'VOB'], 'postag': ['TIME', 'TIME', 'PER', 'p', 'PER', 'n', 'v', 'LOC', 'n', 'PER'], 'prob': [0.79, 0.98, 1.0, 0.49, 0.97, 0.86, 1.0, 0.85, 0.97, 0.99]}]
229+
```
230+
231+
使用ddparser-ernie-1.0进行预测:
224232

225-
# 使用ddparser-ernie-1.0进行预测
233+
```python
226234
ddp = Taskflow("dependency_parsing", model="ddparser-ernie-1.0")
227235
ddp("9月9日上午纳达尔在亚瑟·阿什球场击败俄罗斯球员梅德韦杰夫")
228236
>>> [{'word': ['9月9日', '上午', '纳达尔', '', '亚瑟·阿什球场', '击败', '俄罗斯', '球员', '梅德韦杰夫'], 'head': [2, 6, 6, 5, 6, 0, 8, 9, 6], 'deprel': ['ATT', 'ADV', 'SBV', 'MT', 'ADV', 'HED', 'ATT', 'ATT', 'VOB']}]
229237
```
230238

231-
#### 依存关系可视化
239+
使用分词结果来输入:
232240

233-
句法树可视化示例:
241+
```python
242+
ddp = Taskflow("dependency_parsing")
243+
ddp.from_segments([['9月9日', '上午', '纳达尔', '', '亚瑟·阿什球场', '击败', '俄罗斯', '球员', '梅德韦杰夫']])
244+
>>> [{'word': ['9月9日', '上午', '纳达尔', '', '亚瑟·阿什球场', '击败', '俄罗斯', '球员', '梅德韦杰夫'], 'head': [2, 6, 6, 5, 6, 0, 8, 9, 6], 'deprel': ['ATT', 'ADV', 'SBV', 'MT', 'ADV', 'HED', 'ATT', 'ATT', 'VOB']}]
245+
```
246+
247+
#### 依存关系可视化:
234248

235249
```python
236250
from paddlenlp import Taskflow
@@ -241,6 +255,25 @@ import cv2
241255
cv2.imwrite('test.png', result)
242256
```
243257

258+
#### 标注关系说明:
259+
260+
| Label | 关系类型 | 说明 | 示例 |
261+
| :---: | :--------: | :----------------------- | :----------------------------- |
262+
| SBV | 主谓关系 | 主语与谓词间的关系 | 他送了一本书(他<--送) |
263+
| VOB | 动宾关系 | 宾语与谓词间的关系 | 他送了一本书(送-->书) |
264+
| POB | 介宾关系 | 介词与宾语间的关系 | 我把书卖了(把-->书) |
265+
| ADV | 状中关系 | 状语与中心词间的关系 | 我昨天买书了(昨天<--买) |
266+
| CMP | 动补关系 | 补语与中心词间的关系 | 我都吃完了(吃-->完) |
267+
| ATT | 定中关系 | 定语与中心词间的关系 | 他送了一本书(一本<--书) |
268+
| F | 方位关系 | 方位词与中心词的关系 | 在公园里玩耍(公园-->里) |
269+
| COO | 并列关系 | 同类型词语间关系 | 叔叔阿姨(叔叔-->阿姨) |
270+
| DBL | 兼语结构 | 主谓短语做宾语的结构 | 他请我吃饭(请-->我,请-->吃饭) |
271+
| DOB | 双宾语结构 | 谓语后出现两个宾语 | 他送我一本书(送-->我,送-->书) |
272+
| VV | 连谓结构 | 同主语的多个谓词间关系 | 他外出吃饭(外出-->吃饭) |
273+
| IC | 子句结构 | 两个结构独立或关联的单句 | 你好,书店怎么走?(你好<--走) |
274+
| MT | 虚词成分 | 虚词与中心词间的关系 | 他送了一本书(送-->了) |
275+
| HED | 核心关系 | 指整个句子的核心 | |
276+
244277
#### 可配置参数说明
245278

246279
* `batch_size`:批处理大小,请结合机器情况进行调整,默认为1。
@@ -250,6 +283,7 @@ cv2.imwrite('test.png', result)
250283
* `use_cuda`:是否使用GPU进行切词,默认为False。
251284
* `return_visual`:是否返回句法树的可视化结果,默认为False。
252285

286+
253287
### 情感分析
254288

255289
```python
@@ -384,4 +418,4 @@ from paddlenlp import Taskflow
384418

385419
ner = Taskflow("ner", home_path="/workspace")
386420
```
387-
通过以上方式即可将ner任务相关文件保存至`/workspace`路径下。
421+
通过以上方式即可将ner任务相关文件保存至`/workspace`路径下。

paddlenlp/taskflow/dependency_parsing.py

Lines changed: 68 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -43,33 +43,43 @@
4343
from paddlenlp import Taskflow
4444
4545
ddp = Taskflow("dependency_parsing")
46-
ddp("百度是一家高科技公司")
46+
ddp("三亚是一座美丽的城市")
4747
'''
48-
[{'word': ['百度', '是', '一家', '高科技', '公司'], 'head': ['2', '0', '5', '5', '2'], 'deprel': ['SBV', 'HED', 'ATT', 'ATT', 'VOB']}]
48+
[{'word': ['三亚', '是', '一座', '美丽', '的', '城市'], 'head': [2, 0, 6, 6, 4, 2], 'deprel': ['SBV', 'HED', 'ATT', 'ATT', 'MT', 'VOB']}]
4949
'''
50-
ddp(["百度是一家高科技公司", "他送了一本书"])
51-
'''
52-
[{'word': ['百度', '是', '一家', '高科技', '公司'], 'head': ['2', '0', '5', '5', '2'], 'deprel': ['SBV', 'HED', 'ATT', 'ATT', 'VOB']}, {'word': ['他', '送', '了', '一本', '书'], 'head': ['2', '0', '2', '5', '2'], 'deprel': ['SBV', 'HED', 'MT', 'ATT', 'VOB']}]
50+
ddp(["三亚是一座美丽的城市", "他送了一本书"])
5351
'''
52+
[{'word': ['三亚', '是', '一座', '美丽', '的', '城市'], 'head': [2, 0, 6, 6, 4, 2], 'deprel': ['SBV', 'HED', 'ATT', 'ATT', 'MT', 'VOB']}, {'word': ['他', '送', '了', '一本', '书'], 'head': [2, 0, 2, 5, 2], 'deprel': ['SBV', 'HED', 'MT', 'ATT', 'VOB']}]
53+
'''
5454
5555
ddp = Taskflow("dependency_parsing", prob=True, use_pos=True)
56-
ddp("百度是一家高科技公司")
56+
ddp("三亚是一座美丽的城市")
5757
'''
58-
[{'word': ['百度', '是', '一家', '高科技', '公司'], 'postag': ['ORG', 'v', 'm', 'n', 'n'], 'head': ['2', '0', '5', '5', '2'], 'deprel': ['SBV', 'HED', 'ATT', 'ATT', 'VOB'], 'prob': [1.0, 1.0, 1.0, 1.0, 1.0]}]
58+
[{'word': ['三亚', '是', '一座', '美丽的城市'], 'head': [2, 0, 4, 2], 'deprel': ['SBV', 'HED', 'ATT', 'VOB'], 'postag': ['LOC', 'v', 'm', 'n'], 'prob': [1.0, 1.0, 1.0, 1.0]}]
5959
'''
6060
6161
ddp = Taskflow("dependency_parsing", model="ddparser-ernie-1.0")
62-
ddp("百度是一家高科技公司")
62+
ddp("三亚是一座美丽的城市")
6363
'''
64-
[{'word': ['百度', '是', '一家', '高科技', '公司'], 'head': ['2', '0', '5', '5', '2'], 'deprel': ['SBV', 'HED', 'ATT', 'ATT', 'VOB']}]
64+
[{'word': ['三亚', '是', '一座', '美丽', '的', '城市'], 'head': [2, 0, 6, 6, 4, 2], 'deprel': ['SBV', 'HED', 'ATT', 'ATT', 'MT', 'VOB']}]
6565
'''
6666
6767
ddp = Taskflow("dependency_parsing", model="ddparser-ernie-gram-zh")
68-
ddp("百度是一家高科技公司")
68+
ddp("三亚是一座美丽的城市")
6969
'''
70-
[{'word': ['百度', '是', '一家', '高科技', '公司'], 'head': ['2', '0', '5', '5', '2'], 'deprel': ['SBV', 'HED', 'ATT', 'ATT', 'VOB']}]
70+
[{'word': ['三亚', '是', '一座', '美丽', '的', '城市'], 'head': [2, 0, 6, 6, 4, 2], 'deprel': ['SBV', 'HED', 'ATT', 'ATT', 'MT', 'VOB']}]
7171
'''
7272
73+
# 已分词输入
74+
ddp = Taskflow("dependency_parsing", segmented=True)
75+
ddp.from_segments([["三亚", "是", "一座", "美丽", "的", "城市"]])
76+
'''
77+
[{'word': ['三亚', '是', '一座', '美丽', '的', '城市'], 'head': [2, 0, 6, 6, 4, 2], 'deprel': ['SBV', 'HED', 'ATT', 'ATT', 'MT', 'VOB']}]
78+
'''
79+
ddp.from_segments([['三亚', '是', '一座', '美丽', '的', '城市'], ['他', '送', '了', '一本', '书']])
80+
'''
81+
[{'word': ['三亚', '是', '一座', '美丽', '的', '城市'], 'head': [2, 0, 6, 6, 4, 2], 'deprel': ['SBV', 'HED', 'ATT', 'ATT', 'MT', 'VOB']}, {'word': ['他', '送', '了', '一本', '书'], 'head': [2, 0, 2, 5, 2], 'deprel': ['SBV', 'HED', 'MT', 'ATT', 'VOB']}]
82+
'''
7383
"""
7484

7585

@@ -83,7 +93,7 @@ class DDParserTask(Task):
8393
prob(bool): Whether to return the probability of predicted heads.
8494
use_pos(bool): Whether to return the postag.
8595
batch_size(int): Numbers of examples a batch.
86-
return_visual(bool): If set True, the result will contain the dependency visualization.
96+
return_visual(bool): If True, the result will contain the dependency visualization.
8797
kwargs (dict, optional): Additional keyword arguments passed along to the specific task.
8898
"""
8999

@@ -141,12 +151,27 @@ def __init__(self,
141151

142152
self.use_cuda = use_cuda
143153
self.lac = LAC(mode="lac" if self.use_pos else "seg",
144-
use_cuda=self.use_cuda)
154+
use_cuda=self.use_cuda)
145155
if self.static_mode:
146156
self._get_inference_model()
147157
else:
148158
self._construct_model(model)
149159

160+
def _check_segmented_words(self, inputs):
161+
inputs = inputs[0]
162+
if not all([isinstance(i, list) and i and all(i) for i in inputs]):
163+
raise TypeError("Invalid input format.")
164+
return inputs
165+
166+
def from_segments(self, segmented_words):
167+
segmented_words = self._check_segmented_words(segmented_words)
168+
inputs = {}
169+
inputs['words'] = segmented_words
170+
inputs = self._preprocess_words(inputs)
171+
outputs = self._run_model(inputs)
172+
results = self._postprocess(outputs)
173+
return results
174+
150175
def _construct_input_spec(self):
151176
"""
152177
Construct the input spec for the convert dygraph model to static model.
@@ -182,55 +207,61 @@ def _construct_tokenizer(self, model):
182207
"""
183208
return None
184209

210+
def _preprocess_words(self, inputs):
211+
examples = []
212+
for text in inputs['words']:
213+
example = {"FORM": text}
214+
example = convert_example(
215+
example,
216+
vocabs=[self.word_vocab, self.rel_vocab])
217+
examples.append(example)
218+
219+
batches = [
220+
examples[idx:idx + self.batch_size]
221+
for idx in range(0, len(examples), self.batch_size)
222+
]
223+
224+
def batchify_fn(batch):
225+
raw_batch = [raw for raw in zip(*batch)]
226+
batch = [pad_sequence(data) for data in raw_batch]
227+
return batch
228+
229+
batches = [flat_words(batchify_fn(batch)[0]) for batch in batches]
230+
231+
inputs['data_loader'] = batches
232+
return inputs
233+
185234
def _preprocess(self, inputs):
186235
"""
187236
Transform the raw text to the model inputs, two steps involved:
188237
1) Transform the raw text to token ids.
189238
2) Generate the other model inputs from the raw text and token ids.
190239
"""
191-
inputs = self._check_input_text(inputs)
240+
192241
# Get the config from the kwargs
193242
num_workers = self.kwargs[
194243
'num_workers'] if 'num_workers' in self.kwargs else 0
195244
lazy_load = self.kwargs[
196245
'lazy_load'] if 'lazy_load' in self.kwargs else False
197246

247+
outputs = {}
248+
198249
lac_results = []
199250
position = 0
200251

252+
inputs = self._check_input_text(inputs)
201253
while position < len(inputs):
202254
lac_results += self.lac.run(inputs[position:position +
203-
self.batch_size])
255+
self.batch_size])
204256
position += self.batch_size
205257

206-
outputs = {}
207258
if not self.use_pos:
208259
outputs['words'] = lac_results
209260
else:
210261
outputs['words'], outputs[
211262
'postags'] = [raw for raw in zip(*lac_results)]
212263

213-
examples = []
214-
for text in outputs['words']:
215-
example = {"FORM": text, }
216-
example = convert_example(
217-
example,
218-
vocabs=[self.word_vocab, self.rel_vocab], )
219-
examples.append(example)
220-
221-
batches = [
222-
examples[idx:idx + self.batch_size]
223-
for idx in range(0, len(examples), self.batch_size)
224-
]
225-
226-
def batchify_fn(batch):
227-
raw_batch = [raw for raw in zip(*batch)]
228-
batch = [pad_sequence(data) for data in raw_batch]
229-
return batch
230-
231-
batches = [flat_words(batchify_fn(batch)[0]) for batch in batches]
232-
233-
outputs['data_loader'] = batches
264+
outputs = self._preprocess_words(outputs)
234265
return outputs
235266

236267
def _run_model(self, inputs):

paddlenlp/taskflow/taskflow.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,3 +243,7 @@ def tasks():
243243
"""
244244
task_list = list(TASKS.keys())
245245
return task_list
246+
247+
def from_segments(self, *inputs):
248+
results = self.task_instance.from_segments(inputs)
249+
return results

0 commit comments

Comments
 (0)