Environnement et packages

# activer l'environnement
using Pkg
Pkg. activate("env_julia_sentiment")
  Activating project at `c:\Users\ricco\Desktop\demo\env_julia_sentiment`
# liste des packages
Pkg.status()
Status `C:\Users\ricco\Desktop\demo\env_julia_sentiment\Project.toml`

  [a93c6f00] DataFrames v1.8.2

  [da1fdf0e] FreqTables v1.0.0

  [77b9cbda] TextModels v0.2.1

  [d53efdab] VaderSentiment v0.1.1

  [fdbf4ff8] XLSX v0.11.11

Solution avec VaderSentiment.jl

Un exemple de document à évaluer.

# phrase à tester
phrase_test = "this is a painful story that is very upsetting"
"this is a painful story that is very upsetting"
# https://github.com/nusretipek/VaderSentiment.jl
import VaderSentiment

# récupération de l'objet de calcul
vs = VaderSentiment.SentimentIntensityAnalyzer

# application à la phrase test
result = vs(phrase_test)

# polarités
result.polarity_scores
Dict{String, Float64} with 4 entries:
  "neg"      => 0.473
  "neu"      => 0.527
  "pos"      => 0.0
  "compound" => -0.7425
# accès à "compound" qui est le champ clé
# indiquant le score de positivité global
# on peut le comparer à la valeur seuil 0
result.polarity_scores["compound"]
-0.7425

Corpus IMDB

# packages pour importation
import DataFrames as DFR
import XLSX

# lecture des données
df = DFR.DataFrame(XLSX.readtable("./imbd_reviews_100.xlsx"))

# premières lignes
println(DFR.first(df,5))
5×2 DataFrame

 Row  avis     commentaires                      

      String   String                            

─────┼────────────────────────────────────────────

   1 │ like     Panic in the Streets is a fairly…

   2 │ like     I'm writing this 9 years after t…

   3 │ like     I find this movie the best movie…

   4 │ dislike  While Bondarchuk was by no means…

   5 │ dislike  Oh, man! This thing scared the h…
# distribution des classes
DFR.combine(DFR.groupby(df,:avis),DFR.nrow => :effectif)
2×2 DataFrame
Row avis effectif
String Int64
1 like 51
2 dislike 49

Analyse du corpus IMDB

# documents à traiter
docs = vec(df.commentaires)
typeof(docs)
Vector{String} (alias for Array{String, 1})
# appliquer sur l'ensemble des documents
pred_vader = String[]

# pour chaque document
for d in docs
    # appliquer le système
    res = vs(d)
    # récupérer compound
    score = res.polarity_scores["compound"]
    # ajouter la conclusion avec seuil à 0
    push!(pred_vader, score > 0 ? "like" : "dislike")
end

# 10 premières prédictions
pred_vader[1:10]
10-element Vector{String}:
 "like"
 "like"
 "like"
 "like"
 "dislike"
 "dislike"
 "like"
 "dislike"
 "dislike"
 "dislike"
# matrice de confusion
import FreqTables as FT
FT.freqtable(df.avis,pred_vader)
2×2 Named Matrix{Int64}
Dim1 ╲ Dim2 │ dislike     like
────────────┼─────────────────
dislike     │      28       21
like        │       7       44
# avec un accuracy
import Statistics
Statistics.mean(df.avis .== pred_vader)
0.72

Solution avec TextModels.jl

Traitement du document exemple

import TextModels as TM

# instaciantion
tm = TM.SentimentAnalyzer()

# essai sur notre phrase test
# nécessité de typer la chaîne en StringDocument
# score entre 0 et 1, seuil possible 0.5
res = tm(TM.StringDocument(phrase_test))
print(res)
0.48139954

Traitement du corpus IMDB

# prédiction sur la base IMDB
# appliquer sur l'ensemble des documents
pred_tm = String[]

# pour chaque document
for d in docs
    # appliquer le système
    # nombre de caractères à prendre en compte limité sinon erreur
    score = tm(TM.StringDocument(d[1:min(512,length(d))]))
    # ajouter la conclusion avec seuil théorique à 0.5 ici
    # mais en fait, on peut moduler et gagner en accuracy
    push!(pred_tm, score > 0.4 ? "like" : "dislike")
end

# 10 premières prédictions
pred_tm[1:10]
10-element Vector{String}:
 "like"
 "like"
 "like"
 "dislike"
 "like"
 "like"
 "like"
 "like"
 "like"
 "like"
# matrice de confusion
FT.freqtable(df.avis,pred_tm)
2×2 Named Matrix{Int64}
Dim1 ╲ Dim2 │ dislike     like
────────────┼─────────────────
dislike     │      17       32
like        │       7       44
# et accuracy pas terrible du tout
println("Accuracy = $(Statistics.mean(df.avis .== pred_tm))")
Accuracy = 0.61