Lök, eller inte lök?

The Onion (varning: många artiklar är NSFW) är en satirisk nyhetsorganisation som parodierar traditionella nyhetsmedier. 2014 lanserade The Onion ClickHole (varning: även ofta NSFW), en satirisk nyhetswebbplats som parodierar ”clickbait” -sidor som BuzzFeed. Tack vare Poes lag är det ganska vanligt att människor läser rubrikerna för artiklar från The Onion eller ClickHole och tror att de är sanna, utan att veta att de är avsedda att vara satir. Det omvända händer också med löjligt klingande riktiga nyhetsberättelser – folk tror ofta att de är satir när de inte är det.

Denna förvirring lämpar sig naturligtvis för ett spel – med tanke på en nyhetsrubrik, försök gissa om eller inte det är satir. Denna utmaning handlar om att göra exakt det med ett program.

Med tanke på en nyhetsrubrik (en sträng som bara består av utskrivbara ASCII-tecken och mellanslag), mata ut 1 om rubriken är satir, eller 0 om den inte är det. Din poäng blir antalet korrekta utdata dividerat med det totala antalet rubriker.

Som vanligt standard kryphål (särskilt optimering för testfall ) är inte tillåtna. För att genomdriva detta kommer jag att köra dina program på en uppsättning av 200 dolda testfall (100 från The Onion, 100 från Not The Onion). Din lösning får inte göra mer än 20 procentenheter mindre än din poäng i de offentliga testfallet för att vara giltig.

Testfall

Att komma med testfall för denna utmaning , Jag valde 25 rubriker från The Onion subreddit (där artiklar från The Onion och dess underordnade webbplatser, som ClickHole, publiceras) och 25 rubriker från underredditen Not The Onion (där riktiga nyhetsartiklar som låter som satir publiceras). De enda ändringarna jag gjorde i rubrikerna var att ersätta ”snygga” citat med vanliga ASCII-citat och standardisera stora bokstäver – allt annat lämnas oförändrat från den ursprungliga artikelns rubrik. Varje rubrik är på sin egen rad.

Lökrubrikerna

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 

Inte lökrubrikerna

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 

Kommentarer

  • Your score will be the number of correct outputs divided by the total number of headlines Är bytecount en tie breaker?
  • Jag ’ en liten bit förvirrad. Vilken typ av lösning förväntar du dig? Varje lösning måste ” optimera för testfallet ” något, bar skriver en AI som kan förstå engelska och har sans för humor. Exempelvis upptäcker Arnauld ’ lösning /ly\b/ som bara fungerar för att 25 Onio n rubriker du valde har mer adverb, men för allt jag vet kan du enkelt trippa upp det med ett annat testbatteri. Och vem ’ är att säga att hans koefficienter inte ’ har valt att optimera sin poäng? (Varför skulle inte ’ inte optimera dem?)
  • Det här testbatteriet verkar lite ovanligt. Det ’ är som att be om en klassificerare som kan upptäcka hundar på ett fotografi, men ta dina positiva testfall som bilder på hundar och dina negativa testfall från en Buzzfeed-artikel med titeln ” 25 foton på föremål du ’ Jag svär är hundar, men nej, visar sig att de är ’ t! (# 11 kommer att spränga ditt sinne!) ” Det gör ett svårt nog svårare problem.
  • Inte bara är utmaningen svår utan den är ’ är inte självklart (för mig) vad ’ är skillnaden. Om jag inte kan ’ inte lösa det, kan naturligtvis mitt program ’ inte lösa det (det vill säga medan jag övertygar mig om att det inte ’ t hårdkod för testfall)
  • Tja, jag tillbringade +36 timmar på att träna ett artificiellt neuralt nätverk med brain.js och LSTM, med prover i detta nummer och 100 andra prover av varje typ från tillhandahållna länkar, men resultatet var ’ t tillräckligt bra med nya titlar som inte var ’ inte närvarande i träningssatser. Jag har ’ gjort: P

Svar

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

63,5% (127/200) om dolda testfall

En enkel heuristik baserad på titelns längd, antal mellanslag och användning av -ly suffix.

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

Prova online!

Kommentarer

  • Detta är absurt effektivt för hur enkelt det är.
  • Denna lösning fick 63,5% på de dolda testfallet, så det är giltigt.
  • Inte så enkelt som det var möjligt i början av sandlådan (100%, med kapitaliseringsskillnader innan den standardiserades) men det här är verkligen enkelt.
  • @Mego Bara av nyfikenhet förbättrar den här NSFW-versionen poängen på de dolda testfallet? 🙂
  • @Arnauld 66% med den versionen

Svar

Python 3, 84%

Otestad på dolda testfall.

Detta använder Keras LSTM RNN utbildad i olika rubriker. För att köra det behöver du Keras följande och modellen som jag har gjort tillgänglig på GitHub: repolänk . Du behöver modellen .h5 och ord- / vektormappningarna finns i .pkl. De senaste

Beroenden är:

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 

Inställningarna är:

max_headline_length = 70 word_count = 20740 

Modellen är:

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

Nu för att ladda modellen och ordet inbäddningar:

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

Och koden för att testa om en sträng är från ”NotTheOnion” eller ”TheOnion ”Jag har skrivit en snabb hjälpfunktion som omvandlar strängen till respektive ordinbäddning:

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 

Förklaring

Den här koden kör en modell som analyserar förhållandet mellan ord genom att representera orden som en ”vektor”. Du kan läsa mer om inbäddning av ord här .

Detta utbildas i rubriker men testfallet är uteslutet .

Dessa processer automatiseras efter en hel del bearbetning. Jag har distribuerat den slutliga bearbetade ordlistan som en .pkl men vad som händer i ordinbäddning är att vi först analyserar meningen och isolerar orden.

Efter att vi nu har orden nästa steg är att kunna förstå skillnaderna och likheterna mellan vissa ord t.ex. king och queen kontra duke och duchess. Dessa inbäddningar sker inte mellan de faktiska orden men mellan siffrorna som representerar orden, vilket är det som är lagrat i .pkl -fil. Ord som maskinen inte förstår mappas till ett speciellt ord <UNK> vilket gör att vi kan förstå att det finns ett ord där men att det inte är exakt känt vad meningen är.

Nu när orden kan förstås måste ordsekvensen (rubrik) kunna analyseras. Detta är vad ”LSTM” gör, en LTSM är en typ av ”RNN” -cell som undviker den försvinnande gradienteffekten. Mer enkelt tar det i en ordsekvens och det gör att vi kan hitta relationer mellan dem.

Nu är det sista lagret Dense vilket i grund och botten betyder att det är som en matris som betyder att utdata är som: [probability_is_not_onion, probability_is_onion]. Genom att hitta vilken som är större kan vi välja vilken som är det mest säkra resultatet för den angivna rubriken.

Svar

Python 3 + Keras, 41/50 = 82%

83% (166/200) på dolda testfall

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 och vocabulary.json kan hämtas från här (mycket stort) och här .

Fullt ansluten klassificerare ansluten till en förutbildad sentimentanalysmodell DeepMoji, som består av staplade dubbelriktade LSTM och en uppmärksam mekanism. Jag frös DeepMoji-lagren och tog ut det sista softmax-lagret, tränade bara de helt anslutna lagren och frystade sedan DeepMoji-lagren och tränade dem tillsammans för finjustering. Uppmärksamhetsmekanismen är hämtad från https://github.com/bfelbo/DeepMoji/blob/master/deepmoji/attlayer.py (jag ville inte behöva använda all sin kod som ett beroende för en klass, speciellt eftersom det är Python 2 och ganska svårt att använda som en modul …)

Detta fungerar förvånansvärt dåligt på Megos testuppsättning, med tanke på att det på min egen större validering ställde det blir> 90%. Så jag är inte klar med det här ännu.

Kommentarer

  • 83% på dolda testfall, förutsatt att jag körde det korrekt

Svar

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

96% (192/200) på dolda testfall

 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)); };  

Detta kräver två stora JSON-filer som jag inte kan lägga dem här eller på ” TiO ”. Ladda ned dem från följande länkar och spara dem med words.json och namn, i samma mapp som JS-fil. Det finns också en länk för en JS-fil med testfall och resultat / procentutskrift.Du kan placera dina dolda testfall i onions och nonOnions variabler.

När du har sparat alla 3 filerna i samma katalog kör du node onion.js.

O -funktionen returnerar true om det är lök och false om det inte är det. Använder en stor lista med ordpåsar (utan ordning) för att upptäcka om inmatningssträngen är lök. Typ av hårdkodad men fungerar mycket bra på en mängd slumpmässiga test fall.

Kommentarer

  • Denna lösning får 96% på de dolda testfall

Svar

Arbetar av Arnaulds lösning

JavaScript (ES6), 41/50

64% (128/200) på dold testfall

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) på dolda testfall (ogiltiga)

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 

Längd + ordantal + ”ly ”konceptet fungerar ganska bra, jag kunde pressa ut några fler punkter genom att söka efter ordet” pappa ”(när talar riktiga artiklar om människors pappor i tredje person i titeln?) och en ytterligare punkt genom att ändra ”ly” -sökande heuristik och kontrollera om det finns nummer i titeln (vilket i allmänhet kan vara mindre giltigt utanför testet, så jag lämnade båda lösningarna)

Kommentarer

  • Jag vet inte ’ vet inte om pappadelen … verkar lite som att optimera testfallet för mig …
  • Och ja, jag kan hitta massor av Not Onion-artiklarna som nämner pappor
  • Det ’ är förmodligen ett bättre sätt att göra det som en del av heuristiken och inte bara en hård ” vinner ” om det innehåller pappa, men jag föreställer mig att även utanför testdatabasen abstrakt talar om en specifik ” Pappa ” är vanligare på The Onion
  • Din första lösning fick 64% på dolda testfall, så den är giltig. Din andra lösning fick 62,5% på dolda testfall, så den är inte giltig.
  • @Mego Vilken nära marginal …

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *