Spaces:
Runtime error
Runtime error
add app files
Browse files- deploy/Dockerfile +21 -0
- src/__init__.py +0 -0
- src/app.py +59 -0
- src/meta.py +35 -0
- src/requirements.txt +3 -0
- src/sampler.py +71 -0
deploy/Dockerfile
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.7-slim-buster
|
2 |
+
|
3 |
+
ENV REMOTEPORT 1091
|
4 |
+
RUN pip install --upgrade pip
|
5 |
+
|
6 |
+
RUN useradd --create-home server
|
7 |
+
|
8 |
+
USER server
|
9 |
+
|
10 |
+
ENV PATH="/home/server/.local/bin:${PATH}"
|
11 |
+
|
12 |
+
COPY --chown=server:server src src
|
13 |
+
|
14 |
+
RUN pip install --user -r src/requirements.txt
|
15 |
+
|
16 |
+
## Downloading Belgian GPT2 Model, in order to accelerate startup time
|
17 |
+
RUN python -c "from transformers import GPT2Tokenizer; _ = GPT2Tokenizer.from_pretrained('antoiloui/belgpt2')"
|
18 |
+
RUN python -c "from transformers import GPT2LMHeadModel; _ = GPT2LMHeadModel.from_pretrained('antoiloui/belgpt2')"
|
19 |
+
|
20 |
+
## Running streamlit
|
21 |
+
CMD streamlit run src/app.py --server.port $REMOTEPORT
|
src/__init__.py
ADDED
File without changes
|
src/app.py
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from sampler import GPT2SentencesGenerator, SamplingSentencesGenerator, GreedySentencesGenerator, BeamSentencesGenerator
|
3 |
+
import meta
|
4 |
+
|
5 |
+
st.set_page_config(layout="wide")
|
6 |
+
|
7 |
+
@st.cache(allow_output_mutation = True)
|
8 |
+
def get_sentences_generator(method):
|
9 |
+
model_dir = "antoiloui/belgpt2"
|
10 |
+
if method =='sampling':
|
11 |
+
return SamplingSentencesGenerator(model_dir)
|
12 |
+
elif method == 'greedy':
|
13 |
+
return GreedySentencesGenerator(model_dir)
|
14 |
+
elif method == 'beam':
|
15 |
+
return BeamSentencesGenerator(model_dir)
|
16 |
+
else:
|
17 |
+
return NotImplementedError
|
18 |
+
|
19 |
+
|
20 |
+
def display_parameters_from_generation_method(methode):
|
21 |
+
user_input = st.text_input('Texte de départ (peut être vide)', "les modèles génératifs sont cool !")
|
22 |
+
if methode == 'sampling':
|
23 |
+
user_nsamples = st.number_input("Nombre de phrases", min_value=1, max_value = 20)
|
24 |
+
user_temperature = st.slider("Choisissez une température : le degré de 'folie' du texte", min_value = 0.01, max_value = 1.5, value = 0.7)
|
25 |
+
user_top_k = st.slider(" TOP K : choisissez parmi les K mots les plus probables dans la génération", min_value = 0, max_value = 1000, value=0)
|
26 |
+
user_top_p = st.slider(" TOP P : choisissez parmi le pourcentage des mots les plus probables dans la génération", min_value = 0.5, max_value = 0.99, value = 0.9)
|
27 |
+
args_dict= {"contexte":user_input, "nsamples":user_nsamples, "temperature":user_temperature, "top_p":user_top_p, "top_k":user_top_k}
|
28 |
+
elif methode == 'greedy':
|
29 |
+
args_dict= {"contexte":user_input}
|
30 |
+
elif methode == 'beam':
|
31 |
+
user_num_beams = st.number_input("Nombre de faisceaux de probabilités", min_value = 2, max_value = 10)
|
32 |
+
user_nsamples = st.number_input("Nombre de phrases", min_value=1, max_value = 10)
|
33 |
+
args_dict= {"contexte":user_input, "num_beams":user_num_beams, "nsamples":user_nsamples}
|
34 |
+
else:
|
35 |
+
st.write("Les autres méthodes arrivent !!!")
|
36 |
+
return args_dict
|
37 |
+
|
38 |
+
|
39 |
+
def display_principles():
|
40 |
+
for k,sentences in meta.body.items():
|
41 |
+
st.header(k)
|
42 |
+
for s in sentences:
|
43 |
+
st.write(s)
|
44 |
+
|
45 |
+
def main():
|
46 |
+
st.title(meta.TITLE)
|
47 |
+
display_principles()
|
48 |
+
methode = st.selectbox("Choisissez votre méthode de génération", ['sampling', 'greedy', 'beam'])
|
49 |
+
generator = get_sentences_generator(methode)
|
50 |
+
st.header(f"Paramètres de la méthode __{methode}__")
|
51 |
+
args_dict = display_parameters_from_generation_method(methode)
|
52 |
+
|
53 |
+
if st.button('Parle, beau parleur !'):
|
54 |
+
res = generator.generate(**args_dict)
|
55 |
+
for texte in res:
|
56 |
+
st.write(texte)
|
57 |
+
|
58 |
+
if __name__=='__main__':
|
59 |
+
main()
|
src/meta.py
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from collections import OrderedDict
|
2 |
+
|
3 |
+
TITLE = "Beau parleur 🥖🥖🥖 : plateforme d'expérimentation de modèle génératif en langue française"
|
4 |
+
|
5 |
+
HEADER1 = "Principes et objectifs"
|
6 |
+
HEADER2 = "Méthodes de génération"
|
7 |
+
HEADER3 = "Expérimentons ! "
|
8 |
+
|
9 |
+
body = OrderedDict()
|
10 |
+
|
11 |
+
body[HEADER1] = []
|
12 |
+
body[HEADER1].append("Les modèles génératifs sur base des [Transformers](https://fr.wikipedia.org/wiki/Transformeur) ont permis une avancée notable en ce qui concerne la compréhension automatisée du langage. Ceci a permis des innovations de rupture dans plusieurs domaines, notamment dans la qualification de texte. Mais _quid_ du domaine créatif ? C'est ici qu'interviennent les modèles génératifs tels que [GPT](https://openai.com/blog/language-unsupervised/),[GPT-2](https://openai.com/blog/better-language-models/) ou encore le récent - et révolutionnaire - [GPT3](https://arxiv.org/abs/2005.14165).")
|
13 |
+
|
14 |
+
body[HEADER1].append("Ces modèles sont pré-entraînés : ceci veut dire que les paramètres qui les déterminent ont déjà été appris par une grande volumétrie de données. Ainsi, libre à l'utilisateur de ce modèle de l'utiliser directement, ou bien de _spécialiser_ le modèle en ajoutant une couche d'apprentissage supplémentaire (on parle alors de _fine-tuning_)")
|
15 |
+
|
16 |
+
body[HEADER1].append("Ceci étant, les modèles génératifs sont biaisés en ce qui concerne les langues traitées. Ainsi, la donnée ayant servi à créer les modèles génératifs GPT est principalement de langue anglaise, ce qui peut avoir tendance à entraver l'adoption de l'intelligence artificielle dans le monde dans le monde.")
|
17 |
+
|
18 |
+
|
19 |
+
body[HEADER1].append("La langue française n'est pas épargnée par une telle prépondérance de la langue anglaise. Notons néanmoins la remarquable initiative [PiaF](https://aclanthology.org/2020.lrec-1.673/), initiative issue de l'[EtaLab](https://www.etalab.gouv.fr/politique-de-la-donnee), plateforme gouvernementale de partage de la donnée.")
|
20 |
+
|
21 |
+
body[HEADER1].append("Fort heureusement, la langue française ne se résume pas au territoire hexagonal ! Ainsi Antoine Louis a pré-entraîné un modèle GPT-2 sur près de 60Gb de donnée française et a mis à disposition ce modèle sur Hugging Face. Ce modèle se nomme [BelGPT-2](https://github.com/antoiloui/belgpt2), et c'est celui-ci que nous utiliserons.")
|
22 |
+
|
23 |
+
|
24 |
+
body[HEADER2] = []
|
25 |
+
body[HEADER2].append("Nous allons explorer 3 méthodes de génération. Les personnes intéressées pourront se référer au [post de blog de Hugging Face sur les modèles génératif](https://huggingface.co/blog/how-to-generate)")
|
26 |
+
body[HEADER2].append("En bref, un modèle de type GPT2 est __auto-régressif__: conditionnellement à un mot que l'on prononce au sein d'une phrase, le modèle apprend à déterminer le mot suivant le plus probable.")
|
27 |
+
|
28 |
+
body[HEADER2].append("Trois méthodes de générations sont possibles à partir de ce modèle")
|
29 |
+
body[HEADER2].append(" - Une génération _greedy_ : les mots générés sont les mots les plus probables. Cette méthode n'a pas l'avantage de la diversité, car il n'en découle qu'un seul scénario possible.")
|
30 |
+
body[HEADER2].append(" - Une génération par faisceaux, dite _beam search_ : les mots générés font partie d'une arborescence de mots les plus probables.")
|
31 |
+
body[HEADER2].append(" - Une génération par échantillonage, ou une génération _sampling_ : On échantillone suivant la loi de probabilité calibrée par le modèle.")
|
32 |
+
|
33 |
+
body[HEADER3] = []
|
34 |
+
body[HEADER3].append("Il est grand temps d'expérimenter la génération et de se laisser entraîner par les différentes propositions")
|
35 |
+
body[HEADER3].append("⚠️ Suivant les paramètres d'entrée, les textes générés peuvent être vulgaires, voire offensants. Ceux-ci peuvent être instructifs en ce qui concerne toxicité actuelle des discours sur Internet : en effet, la donnée sur laquelle ces modèles sont appris ⚠️ ")
|
src/requirements.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
streamlit
|
2 |
+
torch
|
3 |
+
transformers
|
src/sampler.py
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# -*- coding: utf-8 -*-
|
2 |
+
import argparse
|
3 |
+
from transformers import CamembertTokenizerFast
|
4 |
+
from transformers import GPT2Config, GPT2LMHeadModel, GPT2Tokenizer
|
5 |
+
from abc import abstractmethod
|
6 |
+
from typing import List
|
7 |
+
|
8 |
+
class GPT2SentencesGenerator():
|
9 |
+
"""Abstract sentences GPT2 class, taking two inputs directly from Huffing Face directory: tokenizer and model"""
|
10 |
+
def __init__(self, model_dir):
|
11 |
+
self.tokenizer = GPT2Tokenizer.from_pretrained(model_dir)
|
12 |
+
self.model = GPT2LMHeadModel.from_pretrained(model_dir)
|
13 |
+
|
14 |
+
@abstractmethod
|
15 |
+
def generate(self):
|
16 |
+
"""Abstract generative method"""
|
17 |
+
pass
|
18 |
+
|
19 |
+
def decode(self, generated: List[int])-> List[str]:
|
20 |
+
""" Decode model output """
|
21 |
+
res = []
|
22 |
+
for v in generated:
|
23 |
+
res.append(self.tokenizer.decode(v, skip_special_tokens = True))
|
24 |
+
return res
|
25 |
+
|
26 |
+
def encode_context(self, contexte:str)->List[int]:
|
27 |
+
"""encodes prompt input with UTF8 handling"""
|
28 |
+
utf8_contexte = contexte.encode("utf8").decode("utf8")
|
29 |
+
input_ids = self.tokenizer.encode(utf8_contexte, return_tensors = "pt")
|
30 |
+
return input_ids
|
31 |
+
|
32 |
+
class GreedySentencesGenerator(GPT2SentencesGenerator):
|
33 |
+
def generate(self, contexte:str)->List[str]:
|
34 |
+
""" Greedy output generation method """
|
35 |
+
input_ids = self.encode_context(contexte)
|
36 |
+
generated = self.model.generate(input_ids, do_sample = False)
|
37 |
+
return self.decode(generated)
|
38 |
+
|
39 |
+
class BeamSentencesGenerator(GPT2SentencesGenerator):
|
40 |
+
def generate(self, contexte:str, nsamples:int, num_beams:int)->List[str]:
|
41 |
+
""" """
|
42 |
+
input_ids = self.encode_context(contexte)
|
43 |
+
generated = self.model.generate(input_ids, do_sample = False, num_beams= num_beams, num_return_sequences = nsamples, early_stopping=True)
|
44 |
+
return self.decode(generated)
|
45 |
+
|
46 |
+
class SamplingSentencesGenerator(GPT2SentencesGenerator):
|
47 |
+
def generate(self, contexte:str, nsamples : int =10, temperature : int = 0.7, top_p: float = 0.9, top_k : float =0)-> List[str]:
|
48 |
+
input_ids = self.encode_context(contexte)
|
49 |
+
generated = self.model.generate(
|
50 |
+
input_ids,
|
51 |
+
do_sample=True,
|
52 |
+
top_p = top_p,
|
53 |
+
top_k = top_k,
|
54 |
+
temperature =temperature,
|
55 |
+
num_return_sequences=nsamples,
|
56 |
+
repetition_penalty = 1.2,
|
57 |
+
early_stopping = True)
|
58 |
+
return self.decode(generated)
|
59 |
+
|
60 |
+
if __name__=='__main__':
|
61 |
+
parser = argparse.ArgumentParser()
|
62 |
+
parser.add_argument("--contexte")
|
63 |
+
parser.add_argument("--model_dir")
|
64 |
+
parser.add_argument("--temperature", type = float, default = 0.7)
|
65 |
+
parser.add_argument("--tensors_type", default = "pt")
|
66 |
+
parser.add_argument("--samples_output", default = "generated_sample.txt")
|
67 |
+
args = parser.parse_args()
|
68 |
+
g = SamplingSentencesGenerator(args.model_dir)
|
69 |
+
res = g.generate(args.contexte, nsamples = 5, temperature = args.temperature)
|
70 |
+
for v in res:
|
71 |
+
print(v)
|