- Notifications
You must be signed in to change notification settings - Fork 4.3k
/
Copy pathhuggingface_language_modeling.py
183 lines (158 loc) · 6.61 KB
/
huggingface_language_modeling.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""A pipeline that uses RunInference to perform Language Modeling with
masked language model from Hugging Face.
This pipeline takes sentences from a custom text file, converts the last word
of the sentence into a <mask> token, and then uses the AutoModelForMaskedLM from
Hugging Face to predict the best word for the masked token given all the words
already in the sentence. The pipeline then writes the prediction to an output
file in which users can then compare against the original sentence.
"""
importargparse
importlogging
fromcollections.abcimportIterable
fromcollections.abcimportIterator
importapache_beamasbeam
importtorch
fromapache_beam.ml.inference.baseimportKeyedModelHandler
fromapache_beam.ml.inference.baseimportPredictionResult
fromapache_beam.ml.inference.baseimportRunInference
fromapache_beam.ml.inference.huggingface_inferenceimportHuggingFaceModelHandlerKeyedTensor
fromapache_beam.options.pipeline_optionsimportPipelineOptions
fromapache_beam.options.pipeline_optionsimportSetupOptions
fromapache_beam.runners.runnerimportPipelineResult
fromtransformersimportAutoModelForMaskedLM
fromtransformersimportAutoTokenizer
defadd_mask_to_last_word(text: str) ->tuple[str, str]:
text_list=text.split()
returntext, ' '.join(text_list[:-2] + ['<mask>', text_list[-1]])
deftokenize_sentence(
text_and_mask: tuple[str, str],
tokenizer: AutoTokenizer) ->tuple[str, dict[str, torch.Tensor]]:
text, masked_text=text_and_mask
tokenized_sentence=tokenizer.encode_plus(masked_text, return_tensors="pt")
# Workaround to manually remove batch dim until we have the feature to
# add optional batching flag.
# TODO(https://github.com/apache/beam/issues/21863): Remove once optional
# batching flag added
returntext, {
k: torch.squeeze(v)
fork, vindict(tokenized_sentence).items()
}
deffilter_empty_lines(text: str) ->Iterator[str]:
iflen(text.strip()) >0:
yieldtext
classPostProcessor(beam.DoFn):
"""Processes the PredictionResult to get the predicted word.
The logits are the output of the Model. We can get the word with the highest
probability of being a candidate replacement word by taking the argmax.
"""
def__init__(self, tokenizer: AutoTokenizer):
super().__init__()
self.tokenizer=tokenizer
defprocess(self, element: tuple[str, PredictionResult]) ->Iterable[str]:
text, prediction_result=element
inputs=prediction_result.example
logits=prediction_result.inference['logits']
mask_token_index=torch.where(
inputs["input_ids"] ==self.tokenizer.mask_token_id)[0]
predicted_token_id=logits[mask_token_index].argmax(axis=-1)
decoded_word=self.tokenizer.decode(predicted_token_id)
yieldtext+';'+decoded_word
defparse_known_args(argv):
"""Parses args for the workflow."""
parser=argparse.ArgumentParser()
parser.add_argument(
'--input',
dest='input',
help='Path to the text file containing sentences.')
parser.add_argument(
'--output',
dest='output',
required=True,
help='Path of file in which to save the output predictions.')
parser.add_argument(
'--model_name',
dest='model_name',
required=True,
help='bert uncased model. This can be base model or large model')
parser.add_argument(
'--model_class',
dest='model_class',
default=AutoModelForMaskedLM,
help='Name of the model from Hugging Face')
parser.add_argument(
'--large_model',
action='store_true',
dest='large_model',
default=False,
help='Set to true if your model is large enough to run into memory '
'pressure if you load multiple copies.')
returnparser.parse_known_args(argv)
defrun(
argv=None, save_main_session=True, test_pipeline=None) ->PipelineResult:
"""
Args:
argv: Command line arguments defined for this example.
save_main_session: Used for internal testing.
test_pipeline: Used for internal testing.
"""
known_args, pipeline_args=parse_known_args(argv)
pipeline_options=PipelineOptions(pipeline_args)
pipeline_options.view_as(SetupOptions).save_main_session=save_main_session
pipeline=test_pipeline
ifnottest_pipeline:
pipeline=beam.Pipeline(options=pipeline_options)
tokenizer=AutoTokenizer.from_pretrained(known_args.model_name)
model_handler=HuggingFaceModelHandlerKeyedTensor(
model_uri=known_args.model_name,
model_class=known_args.model_class,
framework='pt',
max_batch_size=1,
large_model=known_args.large_model)
ifnotknown_args.input:
text= (
pipeline|'CreateSentences'>>beam.Create([
'The capital of France is Paris .',
'It is raining cats and dogs .',
'Today is Monday and tomorrow is Tuesday .',
'There are 5 coconuts on this palm tree .',
'The strongest person in the world is not famous .',
'The secret ingredient to his wonderful life was gratitude .',
'The biggest animal in the world is the whale .',
]))
else:
text= (
pipeline|'ReadSentences'>>beam.io.ReadFromText(known_args.input))
text_and_tokenized_text_tuple= (
text
|'FilterEmptyLines'>>beam.ParDo(filter_empty_lines)
|'AddMask'>>beam.Map(add_mask_to_last_word)
|
'TokenizeSentence'>>beam.Map(lambdax: tokenize_sentence(x, tokenizer)))
output= (
text_and_tokenized_text_tuple
|'RunInference'>>RunInference(KeyedModelHandler(model_handler))
|'ProcessOutput'>>beam.ParDo(PostProcessor(tokenizer=tokenizer)))
_=output|"WriteOutput">>beam.io.WriteToText(
known_args.output, shard_name_template='', append_trailing_newlines=True)
result=pipeline.run()
result.wait_until_finish()
returnresult
if__name__=='__main__':
logging.getLogger().setLevel(logging.INFO)
run()