La cipolla, o non la cipolla?

The Onion (avviso: molti articoli sono NSFW) è unorganizzazione di notizie satiriche che parodia dei media tradizionali. Nel 2014, The Onion ha lanciato ClickHole (avviso: spesso anche NSFW), un sito di notizie satiriche che parodia 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 veri, non sapendo che intendono essere satira. Il contrario accade anche con notizie reali 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, prova a indovinare se o non è satira. Questa sfida consiste nel fare esattamente questo con un programma.

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

Come al solito, scappatoie standard (in particolare ottimizzazione per i casi di test ) non sono consentiti. Per imporlo, eseguirò i tuoi programmi su una serie di 200 casi di test nascosti (100 da The Onion, 100 da Not The Onion). La tua soluzione non deve ottenere più di 20 punti percentuali in meno rispetto al tuo punteggio sui casi di test pubblici per essere valida.

Scenari di test

Per elaborare casi di test per questa sfida , Ho scelto 25 titoli da The Onion subreddit (dove vengono pubblicati articoli di The Onion e dei suoi siti secondari, come ClickHole) e 25 titoli da il subreddit Not The Onion (dove vengono pubblicati articoli di notizie reali che suonano come satira). Le uniche modifiche che ho apportato ai titoli sono state la sostituzione delle citazioni “fantasiose” con citazioni ASCII regolari e la standardizzazione delle maiuscole: tutto il resto è rimasto invariato rispetto al titolo dellarticolo originale. Ogni titolo è su una riga separata.

I titoli di 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 

Not The Onion headlines

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 

Commenti

  • Your score will be the number of correct outputs divided by the total number of headlines bytecount è un elemento decisivo?
  • I ‘ sono un po confuso. Che tipo di soluzione ti aspetti? Ogni soluzione dovrà ” ottimizzare per i casi di test ” in qualche modo, escludendo la scrittura di unintelligenza artificiale in grado di comprendere linglese e con il senso dellumorismo. Ad esempio, la soluzione di Arnauld ‘ rileva /ly\b/ che funziona solo perché il 25 Onio n i titoli che hai scelto hanno più avverbi, ma per quanto ne so potresti facilmente farcela con una batteria di prova diversa. E chi ‘ dice che i suoi coefficienti non sono ‘ scelti per ottimizzare il suo punteggio? (Perché ‘ non li ottimizza?)
  • 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 ‘ ll giuro sono cani, ma no, risulta che non sono ‘ t! (# 11 ti lascerà a bocca aperta!) ” Rende un problema abbastanza difficile più difficile.
  • Non solo la sfida è difficile, ma ‘ è anche non ovvio (per me) quale sia ‘ la differenza. Se non riesco ‘ a risolverlo, ovviamente il mio programma non può ‘ risolverlo (cioè, pur convincendomi che non ‘ t hardcode per i casi di test)
  • Beh, ho trascorso +36 ore ad addestrare una rete neurale artificiale utilizzando brain.js e LSTM, con esempi in questo numero e altri 100 campioni di ogni tipo dai link forniti, ma il risultato non era ‘ abbastanza buono con i nuovi titoli che non erano ‘ t presente nei set di addestramento. Ho ‘ fatto: P

Risposta

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

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

Una semplice euristica basata sulla lunghezza del titolo, numero di spazi e utilizzo del -ly suffisso.

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

Provalo online!

Commenti

  • Questo è assurdamente efficace per quanto sia semplice.
  • Questa soluzione ha ottenuto il 63,5% su i casi di test nascosti, quindi è valido.
  • Non così semplice come era possibile allinizio del sandbox (100%, utilizzando le differenze di maiuscole prima che fosse standardizzato) ma questo è davvero semplice.
  • @Mego Solo per curiosità, questa versione NSFW migliora il punteggio dei casi di test nascosti? 🙂
  • @Arnauld 66% con quella versione

Answer

Python 3, 84%

Non testato su casi di test nascosti.

Utilizza Keras LSTM RNN addestrato su vari titoli. Per eseguirlo hai bisogno di Keras quanto segue e del modello che ho reso disponibile su GitHub: repo link . Avrai bisogno del modello .h5 e le mappature parola / vettore sono in .pkl. Le ultime

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 embeddings:

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 rapida funzione di supporto che converte la stringa nelle rispettive parole incorporate:

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 una modello che analizza le relazioni tra le parole rappresentando le parole come un “vettore”. Puoi trovare ulteriori informazioni sullincorporamento di parole qui .

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

Questo processo è automatizzato dopo un po di elaborazione. Ho distribuito lelenco di parole elaborato finale come .pkl ma ciò che accade nellincorporamento di parole è prima di analizzare la frase e isolare le parole.

Dopo ora avere le parole il passaggio successivo è essere in grado di comprendere le differenze e le somiglianze tra determinate parole, ad esempio king e queen rispetto a duke e duchess. Questi incorporamenti non si verificano tra le parole effettive ma tra i numeri che rappresentano le parole che è ciò che è memorizzato nel .pkl file. Le parole che la macchina non “capisce” vengono mappate su una parola speciale <UNK> che ci permette di capire che cè una parola lì ma che non si sa esattamente quale sia il significato.

Ora che le parole possono essere comprese, la sequenza di parole (titolo) deve poter essere analizzata. Questo è ciò che fa “LSTM”, un LTSM è un tipo di cella “RNN” che evita leffetto sfumato di fuga. Più semplicemente, prende una sequenza di parole e ci permette di trovare le relazioni tra loro.

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

Answer

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.h5 e vocabulary.json possono essere recuperati da qui (molto grande) e qui .

Classificatore completamente connesso collegato a un modello di analisi del sentiment pre-addestrato DeepMoji, che consiste di LSTM bidirezionali impilati e un meccanismo di attenzione. Ho congelato gli strati DeepMoji e ho tolto lultimo strato softmax, allenato solo gli strati completamente collegati, quindi ho scongelato gli strati DeepMoji e li ho addestrati insieme per la messa a punto. Il meccanismo dellattenzione è preso da https://github.com/bfelbo/DeepMoji/blob/master/deepmoji/attlayer.py (non volevo “t volere utilizzare tutto il loro codice come dipendenza per una classe, soprattutto perché è Python 2 ed è piuttosto ingombrante da usare come modulo …)

Questo funziona sorprendentemente male sul set di test di Mego, considerando che sul mio set di convalida più grande lo imposta ottiene> 90%. Quindi non ho ancora finito con questo.

Commenti

  • 83% su casi di test nascosti, supponendo di averlo eseguito correttamente

Risposta

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 posso “mettere qui o su ” TiO “. Scaricali dai seguenti link e salvali con words.json e nomi, nella stessa cartella del file JS. Cè anche un collegamento per un file JS con casi di test e stampa dei risultati / percentuale.Puoi inserire i tuoi casi di test nascosti nelle variabili onions e nonOnions.

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

La funzione O restituirà true se è onion e false se non è “t. Utilizza un ampio elenco di sacchi di parole (senza ordine) per rilevare se la stringa di input è onion. È un po hardcoded, ma funziona molto bene su una varietà di test casuali casi.

Commenti

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

Risposta

Elaborazione della soluzione di Arnauld

JavaScript (ES6), 41/50

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

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 validi)

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 

La lunghezza + il conteggio delle parole + “ly “Il concetto funziona abbastanza bene, sono riuscito a spremere qualche altro punto controllando la parola” Papà “(quando i veri articoli parlano dei padri delle persone nella terza persona nel titolo?) e un ulteriore punto modificando la ricerca “ly” euristica e il controllo della presenza di numeri nel titolo (che potrebbe essere meno valido nel caso generale al di fuori del test, quindi ho lasciato entrambe le soluzioni)

Commenti

  • Non ‘ non so della parte del papà … mi sembra un po come ottimizzare il test case …
  • E sì, posso trovare molti articoli di Not the Onion che menzionano i papà
  • Là ‘ è probabilmente un modo migliore per farlo come parte delleuristica e non solo un duro ” win ” se contiene papà, ma immagino che anche al di fuori del database di test si parli astrattamente di uno specifico ” Dad ” è più comune su The Onion
  • 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 What a close margin …

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *