[Data & crise] Saison #3 E2 — Super Caméléon se lance dans la RSE

Nadia Zabeti
INVYO
Published in
7 min readOct 20, 2021

C’est parti pour la seconde aventure de Super Caméléon! Après l’avoir suivi dans son enquête sur le changement climatique 👇

…vous allez le suivre dans son nouveau challenge: la RSE.

▶️ La Responsabilité Sociétale des Entreprises (RSE), c’est la reconnaissance par une entreprise de la nécessité de s’engager d’un point de vue social et environnemental.

Concrètement, c’est une manière pour une entreprise de montrer qu’elle ne s’intéresse pas seulement à faire du profit, mais qu’elle souhaite avoir un impact positif sur la planète.

C’est donc tout naturellement que Super Caméléon s’est lancé dans ce challenge : intégrer la branche RSE d’une entreprise pour l’accompagner dans cette transformation.

🟢 Pour commencer, voilà le premier défi de Super Caméléon : lire un document pdf parlant de biodiversité et contenant au moins 50 pages. Pourquoi faire ? Pour s’imprégner des différents enjeux sociétaux et environnementaux liés à son activité et pour construire sa propre politique RSE en conséquence.

1. Extraction du texte à partir du pdf

Afin de mener à bien cette mission, il commence par s’attaquer ce rapport d’une grande institution internationale:

https://documents1.worldbank.org/curated/en/445311625065610639/pdf/A-Global-Earth-Economy-Model-to-Assess-Development-Policy-Pathways.pdf

La première étape consiste à extraire l’intégralité du texte du document.

# on importe toutes les librairies nécessaires pour l'ensemble du codeimport time
import unicodedata
from tika import parser
from io import StringIO
from bs4 import BeautifulSoup
from collections import OrderedDict
# on crée les variables qui vont contenir le texte et le temps pour récupérer le textepages_txt = OrderedDict()
start_time = time.time()
# on lit le document pdfdata = parser.from_file(file_name, xmlContent=True)
xhtml_data = BeautifulSoup(data['content'])
# on récupère uniquement le contenu textuel for i, content in enumerate(xhtml_data.find_all('div', attrs={'class': 'page'})):
_buffer = StringIO()
_buffer.write(str(content))
parsed_content = parser.from_buffer(_buffer.getvalue())
text = parsed_content['content']
if text:
text = text.strip()
text = unicodedata.normalize("NFKD", text)
pages_txt["page" + str(i)] = text
full_text = " ".join(list(pages_txt.values()))print("--- %s seconds ---" % (time.time() - start_time), "\n")
print("Extraction completed !")
PDF texts are being generated from A-Global-Earth-Economy-Model-to-Assess-Development-Policy-Pathways.pdf ...
--- 24.97099804878235 seconds --- Extraction completed !

2. Construction des paragraphes

On crée des fonctions pour “nettoyer” les paragraphes : on élimine* les urls, les adresses mail, les caractères non alphanumériques, les mots non porteurs de sens, et les mots à moins de 3 caractères.

regex_url = r"http?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+"regex_email= r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"def clean(text):
# supprime les urls
cleaned = re.sub(regex_url, " ", text)
# supprime les adresses mail
cleaned = re.sub(regex_email, " ", cleaned)
return cleaned
def preprocessing(text):
# supprime les chiffres et autres caractères non alpha
cleaned = re.sub("\d", " ", text)
# supprime les multi space
cleaned = re.sub("\s+", " ", cleaned)
# supprime stop_words
cleaned = remove_stopwords(cleaned)
# supprime les mots à moins de 3 caractères
cleaned = " ".join([token for token in cleaned.split() if len(token)>3])
return cleaned
# permet d'éliminer les paragraphes trop courts (de moins de 17 mots)def filter_paragraph(text):
cleaned = preprocessing(text)
if len(cleaned.split())>17:
return True
else:
return False
# on applique nos fonctionsparagraphs = [clean(p) for p in list_paragraphs_]
paragraphs = [p for p in paragraphs if filter_paragraph(p)]
print(len(paragraphs))
426

3. Identification des paragraphes pertinents

Nous avons maintenant 426 paragraphes porteurs de sens. Il est l’heure de passer à l’étape finale : la détermination des paragraphes qui ont un intérêt pour Super Caméléon.

Méthode n°1 : classification par “zero-shot”

Parmi les méthodes possibles, il y a la classification “zero shot text”. Cette méthode repose sur un algorithme non supervisé**, le modèle ne connaît pas à l’avance les étiquettes qu’on lui demande de reconnaître dans la phase de test.

Concrètement, donnons un texte au modèle et demandons lui d’identifier si ce texte appartient à la catégorie “biodiversité”. On va demander au modèle si notre hypothèse est validée, réfutée ou sans réponse/neutre au regard du contexte (ici l’extrait test_sentence).

label = "biodiversity"test_sentence = """The age of the Earth is about 4.54 billion years.[22][23][24] The earliest undisputed evidence of life on Earth dates at least from 3.5 billion years ago,[25][26][27] during the Eoarchean Era after a geological crust started to solidify following the earlier molten Hadean Eon. There are microbial mat fossils found in 3.48 billion-year-old sandstone discovered in Western Australia.[28][29][30] Other early physical evidence of a biogenic substance is graphite in 3.7 billion-year-old meta-sedimentary rocks discovered in Western Greenland.[31] More recently, in 2015, "remains of biotic life" were found in 4.1 billion-year-old rocks in Western Australia.[32][33] According to one of the researchers, "If life arose relatively quickly on Earth .. then it could be common in the universe."[32]"""hypothesis = f'This example is {label}.'x = tokenizer.encode(test_sentence, hypothesis, return_tensors='pt', truncation=True)
logits = nli_model(x.to(device))[0]
# index 0 : contradiction
# index 1 : neutral
# index 2 : entailment
entail_contradiction_logits = logits[:,[0,2]]
probs = entail_contradiction_logits.softmax(dim=1)
# on calcule la probabilité de l'entailment (si notre phrase appartient à la catégorie "biodiversity")prob_label_is_true = probs[:,1]
if prob_label_is_true[0]>0.5:
print(f"Our paragraph is related to {label}")
print(f'Probability : {prob_label_is_true[0]*100:0.2f}%')
else :
print(f"Our paragraph is not related to {label}")
print(f'Probability : {prob_label_is_true[0]*100:0.2f}%')
Our paragraph is related to biodiversity
Probability : 74.12%

Après application du zero-shot à nos 426 paragraphes initiaux, on obtient 87 paragraphes dont voici un extrait :

'“The Economic Case for Nature”, is part of a series of reports that lays out the economic rationale for investing in nature. Led by the Environment, Natural Resources and Blue Economy (ENB) Global Practice at the World Bank, the series aims to provide analytical insights to inform the process leading up to the 15th Conference of the Parties (COP-15) of the Convention on Biological Diversity, and assist countries implement the new post-2020 global biodiversity framework.',
'The global decline of biodiversity and ecosystem services is a development issue: Economies, particularly in low-income countries, cannot a!ord the risk of collapse in the services provided by nature. The analysis in this report, the first-of- its-kind, shows that by a conservative estimate a collapse in select services such as wild pollination, provision of food from marine fisheries and timber from native forests, could result in a significant decline in global GDP: $2.7 trillion in 2030. Relative impacts are most pronounced in low-income and lower-middle-income countries, where drops in 2030 GDP may be more than 10 percent. — Nature-smart policies can reduce the risk of ecosystem collapse and are “win-win” policies in terms of biodiversity and economic outcomes. A combination of carefully crafted and coordinated policies, particularly those supporting innovation, can simultaneously benefit biodiversity and development.'

Méthode n°2 : classification par similarité

On donne une liste de catégories au modèle, et il nous retourne un score de similarité pour chaque catégorie.

# on importe et télécharge le modèle from sentence_transformers import SentenceTransformer, utilmodel = SentenceTransformer('all-MiniLM-L6-v2')# on utilise le même texte test que dans la méthode 1 (test_sentence) et les mêmes noms de catégories (labels) et on encode égalementtest_sentence_encode = model.encode([test_sentence])
labels_encode = model.encode(labels)
# on calcule la similarité cosine pour chaque labelcos_score = util.pytorch_cos_sim(test_sentence_encode, labels_encode)
label_score_pairs = list(zip(labels, list(cos_score[0])))
label_score_pairs = sorted(label_score_pairs, key=lambda x: x[1], reverse=True)
# on affiche la similarité pour chaque catégoriefor label, score in label_score_pairs:
print("label: {} \t similarity:{}".format(label, score))
label: biodiversity similarity:0.2361169010400772
label: science similarity:0.15425100922584534
label: waste similarity:0.04378325492143631
label: art & culture similarity:0.009952383115887642
label: medicine similarity:0.006353264674544334
label: pharmaceutic similarity:-0.00416538305580616
label: politics similarity:-0.022073637694120407
label: business similarity:-0.08668472617864609

Et en appliquant la méthode à nos 426 paragraphes initiaux (on choisit les paragraphes dont la similarité avec “biodiversité” est la plus grande), on se retrouve avec 178 paragraphes dont voici un extrait :

'The global decline of biodiversity and ecosystem services is a development issue: Economies, particularly in low-income countries, cannot a!ord the risk of collapse in the services provided by nature. The analysis in this report, the first-of-its-kind, shows that by a conservative estimate a collapse in select services such as wild pollination, provision of food from marine fisheries and timber from native forests, could result in a significant decline in global GDP: $2.7 trillion in 2030. Relative impacts are most pronounced in low-income and lower-middle-income countries, where drops in 2030 GDP may be more than 10 percent. —Nature-smart policies can reduce the risk of ecosystem collapse and are “win-win” policies in terms of biodiversity and economic outcomes. A combination of carefully crafted and coordinated policies, particularly those supporting innovation, can simultaneously benefit biodiversity and development. The policies considered in this report reduce.'

Et dire que ces techniques peuvent s’appliquer sur une infinité de PDFs… 🤩

Et si Super Caméléon pouvait aller plus loin encore, et faire un résumé de ces paragraphes qui l’intéressent ? 😎

Rdv le mois prochain pour en savoir plus 🌱

A très bientôt,

*Pour un rafraichissement sur ce qu’est un algorithme non supervisé, voir https://medium.com/invyo/data-crise-saison-2-e4-super-monkey-voyage-dans-lunivers-du-marketing-e0a6d01ae471

**Dans le cadre de la rédaction de cet article, on a choisi des regex simples mais en pratique, il faudrait des regex beaucoup plus sophistiqués pour matcher l’ensembles des urls/mails, mais on a pas voulu vous faire peur 🤯

Merci à @Enzo Ramirez pour le code 🚀

--

--