Wnioskowanie w warunkach niepewności

1 Dane

Dane zaczerpnięte zostały z serwisu Kaggle[fn::https://www.kaggle.com/datasets/paradisejoy/top-hits-spotify-from-20002019]. Dotyczą one najpopularniejszych piosenek w serwisie Spotify w latach 2000-2019.

2 Dostępne kolumny

Dostępnymi kolumnami w tym zbiorze danych są artist song duration_ms explicit year popularity danceability energy key loudness mode speechiness acousticness instrumentalness liveness valence tempo genre. Jest jednak istotny problem — dany utwór może znajdować się w wielu kategoriach. Na potrzeby zadania pierwszy gatunek przypisany do danego utworu został uznany za właściwy.

3 Wczytanie danych

library(bnlearn)
df <- data.frame(read.csv("./songs_normalize.csv"))
colnames(df)
head(df[c("artist",  "song", "genre")], 3)

4 Zamiana gatunków na factory

library(stringr)
library(forcats)
one_gen <- function(entry)
  str_split(entry, ", ")[[1]][1]
df$genre <- as_factor(sapply(c(df$genre), one_gen))
df$explicit <- df$explicit == "True"
head(df[c("artist",  "song", "genre")], 3)
par(mfrow = c(2, 1))
hist(df$instrumentalness)
hist(log(df$instrumentalness))

5 Porównanie $log{\mathrm{instrumentalness}}$

./hists.pdf

6 Przygotowanie do dyskretyzacji

Większość danych ma charakter ciągły, w związku z tym należało je zdyskretyzować. Część danych należało przetworzyć:

  • Wyłączyć artystów i tytuły z czynników
  • Skonwertować liczby całkowite do zmiennoprzecinkowych
  • W przypadku instrumentalności zlogarytmować i usunąć nieskończoności.

7 Przygotowanie do dyskretyzacji

df$instrumentalness <- log(df$instrumentalness)
df$instrumentalness[df$instrumentalness == -Inf] <- min(df[df$instrumentalness != -Inf, ]$instrumentalness) - 1
explicit <- df$explicit
year <- df$year
key <- df$key
mode <- df$mode
instrumentalness <- df$instrumentalness

8 Dyskretyzacja i finalna ramka danych

Instrumentalność została zdyskretyzowana używając metody interwałowej, ponieważ kwantylowa generowała przedziały o długości 0, co jest błędem.

df <- discretize(subset(df, select = -c(explicit, year, key, mode, instrumentalness)), breaks = 10)
df$explicit <- as.factor(explicit)
df$year <- as.factor(year)
df$key <- as.factor(key)
df$mode <- as.factor(mode)
tmp <- discretize(data.frame(instrumentalness), breaks = 10, method = "interval")
df$instrumentalness <- tmp$instrumentalness

graph.pdf

9 Uczenie struktury — GS

gs_graph <- gs(df)
graphviz.plot(gs_graph, layout = "dot")

graph_gs.pdf

10 Uczenie struktury — PC.STABLE

pc_stable_graph <- pc.stable(df)
graphviz.plot(pc_stable_graph, layout = "dot")

graph_pc.pdf

11 Przygotowanie sieci do uczenia parametrów

W tym celu skierowano odpowiednie krawędzie — algorytm GS jako jedyny miał wszystkie krawędzie skierowane.

# IAMB
iamb_graph <- set.arc(iamb_graph, "tempo", "speechiness")
iamb_graph <- set.arc(iamb_graph, "key", "mode")
iamb_graph <- set.arc(iamb_graph, "year", "popularity")
# PC.STABLE
pc_stable_graph <- set.arc(pc_stable_graph, "loudness", "energy")
pc_stable_graph <- set.arc(pc_stable_graph, "key", "mode")

12 Porównanie straty

Chcemy uzyskać jak najniższą wartość straty w naszej sieci, dlatego wybieramy algorytm o najniższej jej wartości.

c(loss(bn.cv(df, iamb_graph)), loss(bn.cv(df, gs_graph)), loss(bn.cv(df, pc_stable_graph)))

Najlepszym okazał się algorytm iamb

13 Uczenie parametrów — mle

Uczenie parametrów z użyciem metody mle jest domyślną metodą — w praktyce nie jest konieczne użycie parametru method, jednak został wprowadzony dla dodatkowej czytelności.

fit_mle <- bn.fit(iamb_graph, df, method = "mle")

14 Wnioskowanie — wulgarność

library(gRain)
junc <- compile(as.grain(fit_mle))
querygrain(setEvidence(junc, evidence = list(explicit="yes")), nodes=c("genre", "speeciness"), type="conditional")
ev_explicit <- querygrain(setEvidence(junc, evidence=list(explicit="yes")))
plot(ev_explicit$genre)

15 Wnioskowanie — wulgarność

Okazuje się, że największą szansę na bycie niecenzuralnymi mają piosenki (w kolejności prawdopodobieństwa): a) pop, b) hip-hop, c) rock, natomiast pozostałe mają bardzo niską zależność.

explicit.pdf

16 Wnioskowanie — energy

Głośność w pewnym stopniu zależy od energii utworu, jednak są to wartości stosunkowo niskie.

ev_energy <- querygrain(setEvidence(junc, evidence=list(energy="(0.861,0.906]")))
plot(ev_energy$loudness)

energy.pdf

17 Przykład wnioskowania

Niech $B$ będzie zdarzeniem $\text{key}=0$, natomiast $A$ zdarzeniem $\text{mode}=1$ \begin{align*} P(A|B)≈0.33
P(B)≈0.099\ P(A)≈ 0.4465 \ P(B|A)&= \frac{P(A|B)P(B)}{P(A)}\ &= \frac{0.33⋅ 0.099}{0.4465}=0.073169 \end{align*}

Wynik jest zgodny z obliczeniami sieci ($0.07390817$). Różnica wynika z błędu zaokrągleń.