La cipolla o no la cipolla?


11

The Onion (attenzione: molti articoli sono NSFW) è un'organizzazione satirica di notizie che parodistica dei media tradizionali. Nel 2014, The Onion ha lanciato ClickHole (attenzione: anche frequentemente NSFW), un sito Web di notizie satiriche che parodia di siti "clickbait" come BuzzFeed. Grazie alla legge di Poe , è abbastanza comune che le persone leggano i titoli degli articoli di The Onion o ClickHole e credano che siano vere, non sapendo che sono intese come satira. Il contrario si verifica anche con notizie dal suono ridicolo: le persone spesso pensano di essere satira quando non lo sono.

Questa confusione si presta naturalmente a un gioco: dato il titolo di una notizia, cerca di indovinare se si tratta o meno di satira. Questa sfida consiste nel fare esattamente questo con un programma.

Dato un titolo di notizie (una stringa composta solo da caratteri e spazi ASCII stampabili), viene emesso 1se il titolo è satira o 0se non lo è. Il tuo punteggio sarà il numero di risultati corretti diviso per il numero totale di titoli.

Come al solito, non sono consentite scappatoie standard (soprattutto l' ottimizzazione per i casi di test ). Per far questo, eseguirò i tuoi programmi su un set di 200 casi di test nascosti (100 da The Onion, 100 da Not The Onion). Per essere valida, la tua soluzione non deve segnare più di 20 punti percentuali in meno del tuo punteggio nei casi di test pubblici.

Casi test

Per trovare casi di test per questa sfida, ho scelto 25 titoli da The Onion subreddit (dove sono pubblicati articoli da The Onion e dai suoi siti figlio, come ClickHole), e 25 titoli da Not The Onion subreddit (dove articoli di notizie reali sembra che siano pubblicati satira). Le uniche modifiche che ho apportato ai titoli sono state la sostituzione di virgolette "fantasiose" con virgolette ASCII regolari e la standardizzazione delle maiuscole: tutto il resto è rimasto invariato rispetto al titolo dell'articolo originale. Ogni titolo è sulla sua linea.

I titoli della cipolla

Trump Warns Removing Confederate Statues Could Be Slippery Slope To Eliminating Racism Entirely
'No Way To Prevent This,' Says Only Nation Where This Regularly Happens
My Doctor Told Me I Should Vaccinate My Children, But Then Someone Much Louder Than My Doctor Told Me I Shouldn't
Man At Park Who Set Up Table Full Of Water Cups Has No Idea How Passing Marathon Runners Got Impression They Can Take Them
This Child Would Have Turned 6 Today If His Mother Hadn't Given Birth To Him In October
Incredible Realism: The Campaign In The Next 'Call Of Duty' Will Begin At Your Avatar's High School Cafeteria When He's Being Tricked Into Joining The Military By A Recruiter
'Sometimes Things Have To Get Worse Before They Get Better,' Says Man Who Accidentally Turned Shower Knob Wrong Way
Report: Uttering Phrase 'Easy Does It' Prevents 78% Of Drywall Damage While Moving Furniture
Barbara Bush Passes Away Surrounded By Loved Ones, Jeb
Family Has Way Too Many Daughters For Them Not To Have Been Trying For Son
News: Privacy Win! Facebook Is Adding A 'Protect My Data' Button That Does Nothing But Feels Good To Press
Dalai Lama Announces Next Life To Be His Last Before Retirement
Researchers Find Decline In Facebook Use Could Be Directly Linked To Desire To Be Happy, Fully Functioning Person
Manager Of Combination Taco Bell/KFC Secretly Considers It Mostly A Taco Bell
Trump: 'It's My Honor To Deliver The First-Ever State Of The Union'
Daring To Dream: Jeff Bezos Is Standing Outside A Guitar Center Gazing Longingly At A $200 Billion Guitar
Area Dad Looking To Get Average Phone Call With Adult Son Down To 47.5 Seconds
Experts Warn Beef Could Act As Gateway Meat To Human Flesh
Jeff Bezos Named Amazon Employee Of The Month
Dad Suggests Arriving At Airport 14 Hours Early
Report: Only 3% Of Conversations Actually Need To Happen
Delta Pilot Refuses To Land Until Gun Control Legislation Passed
Family Wishes Dad Could Find Healthier Way To Express Emotions Than Bursting Into Full-Blown Musical Number
New Honda Commercial Openly Says Your Kids Will Die In A Car Crash If You Buy A Different Brand
Teacher Frustrated No One In Beginner Yoga Class Can Focus Chakras Into Energy Blast

Non i titoli di The Onion

Man Rescued From Taliban Didn't Believe Donald Trump Was President
Nat Geo Hires Jeff Goldblum To Walk Around, Being Professionally Fascinated By Things
Mike Pence Once Ratted Out His Fraternity Brothers For Having A Keg
Reddit CEO Tells User, "We Are Not The Thought Police," Then Suspends That User
Trump Dedicates Golf Trophy To Hurricane Victims
Uber's Search For A Female CEO Has Been Narrowed Down To 3 Men
ICE Director: ICE Can't Be Compared To Nazis Since We're Just Following Orders
Passenger Turned Away From Two Flights After Wearing 10 Layers Of Clothing To Avoid Luggage Fee
Somali Militant Group Al-Shabaab Announces Ban On Single-Use Plastic Bags
UPS Loses Family's $846k Inheritance, Offers To Refund $32 Shipping Fee
Teen Suspended From High School After Her Anti-Bullying Video Hurts Principal's Feelings
Alabama Lawmaker: We Shouldn't Arm Teachers Because Most Are Women
Cat Named After Notorious B.I.G. Shot Multiple Times - And Survives
EPA Head Says He Needs To Fly First Class Because People Are Mean To Him In Coach
Apology After Japanese Train Departs 20 Seconds Early
Justin Bieber Banned From China In Order To 'Purify' Nation
Alcohol Level In Air At Fraternity Party Registers On Breathalyzer
NPR Tweets The Declaration Of Independence, And People Freak Out About A 'Revolution'
Man Who Mowed Lawn With Tornado Behind Him Says He 'Was Keeping An Eye On It.'
After Eating Chipotle For 500 Days, An Ohio Man Says He's Ready For Something New
'El Chapo' Promises Not To Kill Any Jurors From Upcoming Federal Trial
After 4th DWI, Man Argues Legal Limit Discriminates Against Alcoholics
Palestinian Judge Bans Divorce During Ramadan Because 'People Make Hasty Decisions When They're Hungry'
Argentinian Officers Fired After Claiming Mice Ate Half A Ton Of Missing Marijuana
'Nobody Kill Anybody': Murder-Free Weekend Urged In Baltimore

6
Your score will be the number of correct outputs divided by the total number of headlinesBytecount è un pareggio?
Skidsdev,

9
Sono un po 'confuso. Che tipo di soluzione ti aspetti? Ogni soluzione dovrà "ottimizzare in qualche modo per i casi di test", tranne scrivere un'intelligenza artificiale in grado di comprendere l'inglese e che abbia un senso dell'umorismo. Ad esempio, la soluzione di Arnauld rileva /ly\b/che funziona solo perché i 25 titoli di Cipolle che hai scelto hanno più avverbi, ma per quanto ne so potresti facilmente inciampare con una batteria di prova diversa. E chi può dire che i suoi coefficienti non sono stati scelti per ottimizzare il suo punteggio? (Perché non dovrebbe ottimizzarli?)
Lynn,

10
Questa batteria di prova sembra un po 'insolita. È come chiedere un classificatore in grado di rilevare i cani in una fotografia, ma prendere i tuoi casi di test positivi come foto di cani e i tuoi casi di test negativi da un articolo di Buzzfeed intitolato "25 foto di oggetti che giurerai sono cani, ma no, gira Out They Are't! (# 11 ti lascerà a bocca aperta!) "Rende un problema abbastanza difficile.
Sophia Lechner,

4
Non solo la sfida è difficile, ma è anche ovvio (per me) qual è la differenza. Se non riesco a risolverlo, ovviamente il mio programma non può risolverlo (vale a dire, mentre mi convince che non è hardcode per i casi di test)
user202729

4
Bene, ho trascorso +36 ore ad addestrare una rete neurale artificiale usando brain.jse LSTM, con campioni in questo numero e altri 100 campioni di ogni tipo dai link forniti, ma il risultato non era abbastanza buono con nuovi titoli che non erano presenti nei set di addestramento . Ho finito: P
Night2

Risposte:


7

JavaScript (ES7), 39/50 (78%)

63,5% (127/200) su casi di test nascosti

Una semplice euristica basata sulla lunghezza del titolo, il numero di spazi e l'uso del -lysuffisso.

isOnion = str =>
  str.length ** 0.25 +
  str.split(' ').length ** 1.25 * 2 +
  str.split(/ly\b/).length ** 1.75 * 7
  > 76

Provalo online!


Questo è assurdamente efficace per quanto sia semplice.
Don Mille

Questa soluzione ha ottenuto il 63,5% sui casi di test nascosti, quindi è valida.
Mego

Non semplice come era possibile all'inizio del sandbox (100%, utilizzando le differenze di capitalizzazione prima che fosse standardizzato), ma questo è davvero semplice.
Zacharý,

@Mego Solo per curiosità, questa versione del NSFW migliora il punteggio nei casi di test nascosti? :)
Arnauld

@Arnauld 66% con quella versione
Mego

6

Python 3, 84%

Non testato su casi di test nascosti.

Questo utilizza Keras LSTM RNN addestrato su vari titoli. Per eseguirlo è necessario Keras quanto segue e il modello che ho reso disponibile su GitHub: collegamento repo . Sarà necessario il modello .h5e le mappature parola / vettore sono presenti .pkl. L'ultimo

Le dipendenze sono:

import numpy as np
from pickle import load
from keras.preprocessing import sequence, text
from keras.models import Sequential
from keras.layers import Dense, Embedding, SpatialDropout1D, LSTM, Dropout
from keras.regularizers import l2
import re

Le impostazioni sono:

max_headline_length = 70
word_count = 20740

Il modello è:

model = Sequential()
model.add(Embedding(word_count, 32, input_length=max_headline_length))
model.add(SpatialDropout1D(0.4))
model.add(LSTM(64, kernel_regularizer=l2(0.005), dropout=0.3, recurrent_dropout=0.3))
model.add(Dropout(0.5))
model.add(Dense(32, kernel_regularizer=l2(0.005)))
model.add(Dropout(0.5))
model.add(Dense(2, kernel_regularizer=l2(0.001), activation='softmax'))

Ora per caricare il modello e la parola incorporamenti:

model.load_weights('model.h5')
word_to_index = load(open('words.pkl', 'rb'))

E il codice per verificare se una stringa proviene da 'NotTheOnion' o 'TheOnion' ho scritto una funzione di supporto rapido che converte la stringa nelle rispettive incorporazioni di parole:

def get_words(string):
  words = []
  for word in re.finditer("[a-z]+|[\"'.;/!?]", string.lower()):
    words.append(word.group(0))
  return words

def words_to_indexes(words):
  return [word_to_index.get(word, 0) for word in words]

def format_input(word_indexes):
  return sequence.pad_sequences([word_indexes], maxlen=max_headline_length)[0]

def get_type(string):
  words = words_to_indexes(get_words(string))
  result = model.predict(np.array([format_input(words)]))[0]

  if result[0] > result[1]:
    site = 'NotTheOnion'
  else:
    site = 'TheOnion'

  return site

Spiegazione

Questo codice esegue un modello che analizza le relazioni tra le parole rappresentandole come "vettore". Puoi saperne di più sull'incorporamento delle parole qui .

Questo è addestrato sui titoli, ma i casi di test sono esclusi .

Questo processo è automatizzato dopo un bel po 'di elaborazione. Ho distribuito l'elenco finale delle parole elaborate come un, .pklma ciò che accade nell'incorporamento delle parole è prima di tutto analizziamo la frase e isoliamo le parole.

Dopo che ora abbiamo le parole, il passo successivo è quello di essere in grado di comprendere le differenze e le somiglianze tra determinate parole, ad esempio kinge queencontro dukee duchess. Questi incorporamenti non si verificano tra le parole reali ma tra numeri che rappresentano le parole che è ciò che è memorizzato nel .pklfile. Le parole che la macchina non comprende sono associate a una parola speciale <UNK>che ci consente di capire che esiste una parola ma che non si sa esattamente quale sia il significato.

Ora che le parole sono in grado di essere comprese, la sequenza di parole (titolo) deve essere in grado di essere analizzata. Questo è ciò che fa 'LSTM', un LTSM è un tipo di cella 'RNN' che evita l'effetto sfumato sfumato. Più semplicemente, comprende una sequenza di parole e ci consente di trovare relazioni tra di loro.

Ora lo strato finale è Denseche in pratica significa che è un po 'come un array significato la stampa ricorda: [probability_is_not_onion, probability_is_onion]. Trovando quale è più grande possiamo scegliere quale è il risultato più sicuro per il titolo dato.


3

Python 3 + Keras, 41/50 = 82%

83% (166/200) su casi di test nascosti

import json
import keras
import numpy
import re

from keras import backend as K

STRIP_PUNCTUATION = re.compile(r"[^a-z0-9 ]+")


class AttentionWeightedAverage(keras.engine.Layer):
    def __init__(self, return_attention=False, **kwargs):
        self.init = keras.initializers.get("uniform")
        self.supports_masking = True
        self.return_attention = return_attention
        super(AttentionWeightedAverage, self).__init__(**kwargs)

    def build(self, input_shape):
        self.input_spec = [keras.engine.InputSpec(ndim=3)]
        assert len(input_shape) == 3

        self.W = self.add_weight(shape=(input_shape[2], 1),
                                 name="{}_W".format(self.name),
                                 initializer=self.init)
        self.trainable_weights = [self.W]

        super(AttentionWeightedAverage, self).build(input_shape)

    def call(self, x, mask=None):
        logits = K.dot(x, self.W)
        x_shape = K.shape(x)
        logits = K.reshape(logits, (x_shape[0], x_shape[1]))

        ai = K.exp(logits - K.max(logits, axis=-1, keepdims=True))

        if mask is not None:
            mask = K.cast(mask, K.floatx())
            ai = ai * mask

        att_weights = ai / (K.sum(ai, axis=1, keepdims=True) + K.epsilon())
        weighted_input = x * K.expand_dims(att_weights)

        result = K.sum(weighted_input, axis=1)

        if self.return_attention:
            return [result, att_weights]

        return result

    def get_output_shape_for(self, input_shape):
        return self.compute_output_shape(input_shape)

    def compute_output_shape(self, input_shape):
        output_len = input_shape[2]

        if self.return_attention:
            return [(input_shape[0], output_len), (input_shape[0], input_shape[1])]

        return (input_shape[0], output_len)

    def compute_mask(self, input, input_mask=None):
        if isinstance(input_mask, list):
            return [None] * len(input_mask)
        else:
            return None


if __name__ == "__main__":
    model = keras.models.load_model("combined.h5", custom_objects={"AttentionWeightedAverage": AttentionWeightedAverage})
    with open("vocabulary.json", "r") as fh:
        vocab = json.load(fh)

    while True:
        try:
            headline = input()
        except EOFError:
            break

        tokens = STRIP_PUNCTUATION.sub("", headline.lower()).split()

        inp = numpy.zeros((1, 45))

        for i, token in enumerate(tokens):
            try:
                inp[0,i] = vocab[token]
            except KeyError:
                inp[0,i] = 1

        print(model.predict(inp)[0][0] > 0.3)

combined.h5e vocabulary.jsonpuò essere recuperato da qui (molto grande) e qui .

Classificatore completamente connesso collegato a un modello di analisi del sentimento pre-addestrato DeepMoji, che consiste in LSTM bidirezionali impilati e un meccanismo attenzionale. Ho congelato gli strati di DeepMoji ed ho eliminato l'ultimo strato di softmax, ho allenato solo i livelli completamente collegati, quindi ho sbloccato gli strati di DeepMoji e li ho addestrati insieme per la messa a punto. Il meccanismo dell'attenzione è tratto da https://github.com/bfelbo/DeepMoji/blob/master/deepmoji/attlayer.py (non volevo usare tutto il loro codice come dipendenza per una classe, soprattutto perché è Python 2 e piuttosto ingombrante da usare come modulo ...)

Ciò si comporta sorprendentemente male sul set di test di Mego, considerando che sul mio set di validazione più grande ottiene> 90%. Quindi non ho ancora finito con questo.


83% su casi di test nascosti, supponendo che l'ho eseguito correttamente
Mego

1

JavaScript ( Node.js ), 98% (49/50)

96% (192/200) su casi di test nascosti

const words = require('./words');
const bags = require('./bags');

let W = s => s.replace(/[^A-Za-z0-9 ]/g, '').toLowerCase().split(' ').filter(w => w.length > 3);

let M = b => {
    for (let i = 0; i < bags.length; i++) {
        let f = true;
        for (let j = 0; j < bags[i].length; j++) if (!b.includes(bags[i][j])) {
            f = false;
            break;
        }
        if (f) return true;
    }
    return false;
};

let O = s => {
    let b = [];
    W(s).forEach(w => {
        let p = words.indexOf(w);
        if (p >= 0) b.push(p);
    });
    return (b.length > 0 && M(b));
};

Ciò richiede due file JSON di grandi dimensioni che non riesco a metterli qui o su "TiO". Si prega di scaricarli dai seguenti collegamenti e salvarli con i nomi words.jsone bags.json, nella stessa cartella del file JS. Esiste anche un collegamento per un file JS con casi di test e stampa risultato / percentuale. È possibile inserire i casi di test nascosti in onionse nonOnionsvariabili.

Dopo aver salvato tutti e 3 i file nella stessa directory, esegui node onion.js.

La Ofunzione tornerà truese è cipolla e falsese non lo è. Utilizza un grande elenco di word bag (senza ordine) per rilevare se la stringa di input è cipolla. Tipo di hard coded, ma funziona molto bene su una varietà di casi di test casuali.


Questa soluzione ottiene il 96% sui casi di test nascosti
Mego

0

Elaborazione della soluzione di Arnauld

JavaScript (ES6), 41/50

64% (128/200) su casi di test nascosti

str.includes("Dad") || str.length ** .25 +
  str.split(' ').length ** 1.25 * 2 +
  str.split(/ly\b/).length ** 1.75 * 7
 > 76

JavaScript (ES6), 42/50

62,5% (125/200) su casi di test nascosti (non valido)

isOnion = str =>
  str.includes("Dad") || str.length ** .25 +
  str.split(' ').length ** 1.25 * 2 +
  str.split(' ').filter(w => w.length > 3 && w.split(/ly/).length > 1).length * 23.54 +
 /\d/.test(str) * 8
 > 76

Il concetto lunghezza + conteggio parole + "ly" funziona abbastanza bene, sono stato in grado di spremere qualche altro punto controllando la parola "Papà" (quando articoli reali parlano dei padri della gente nella terza persona nel titolo?) E un ulteriore punto modificando l'euristica della ricerca "ly" e verificando la presenza di numeri nel titolo (che potrebbe essere meno valido nel caso generale al di fuori del test, quindi ho lasciato entrambe le soluzioni)


Non conosco la parte di papà ... mi sembra un po 'come ottimizzare il test case per me ...
Don Mille

E sì, posso trovare molti articoli su Non la cipolla che menzionano i papà
Don Mille

Probabilmente c'è un modo migliore per farlo come parte dell'euristica e non solo una "vittoria" difficile se contiene papà, ma immagino che anche fuori dal database di test parlare astrattamente di uno specifico "Papà" sia più comune su The Onion
TiKevin83

La tua prima soluzione ha ottenuto il 64% sui casi di test nascosti, quindi è valida. La tua seconda soluzione ha ottenuto il 62,5% sui casi di test nascosti, quindi non è valida.
Mego

@Mego Che margine stretto ...
user202729
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.