Cebula czy nie cebula?

The Onion (ostrzeżenie: wiele artykułów to NSFW) to satyryczna organizacja informacyjna, parodiuje tradycyjne media informacyjne. W 2014 r. The Onion uruchomił ClickHole (ostrzeżenie: często również NSFW), satyryczną witrynę z wiadomościami, która parodiuje witryny typu „przynęty na kliknięcia”, takie jak BuzzFeed. Dzięki prawu Poe dość często zdarza się, że ludzie czytają nagłówki artykułów z The Onion lub ClickHole i wierzą, że są prawdziwe, nie wiedząc że mają być satyrą. Odwrotna sytuacja ma miejsce również w przypadku śmiesznie brzmiących prawdziwych wiadomości – ludzie często myślą, że są satyrą, kiedy tak nie jest.

To zamieszanie naturalnie nadaje się do gry – biorąc pod uwagę nagłówek wiadomości, spróbuj zgadnąć, czy nie jest to satyra. To wyzwanie polega na zrobieniu dokładnie tego z programem.

Biorąc pod uwagę nagłówek wiadomości (ciąg składający się tylko z drukowalnych znaków ASCII i spacji), wypisz 1, jeśli nagłówek to satyra lub 0, jeśli nie jest. Twoim wynikiem będzie liczba poprawnych wyników podzielona przez całkowitą liczbę nagłówków.

Jak zwykle, standardowe luki (zwłaszcza optymalizacja pod kątem przypadków testowych ) są niedozwolone. Aby to wymusić, uruchomię Twoje programy na zestawie 200 ukrytych przypadków testowych (100 z The Onion, 100 z Not The Onion). Twoje rozwiązanie musi uzyskać nie więcej niż 20 punktów procentowych mniej niż Twój wynik w publicznych przypadkach testowych, aby było ważne.

Przypadki testowe

Aby wymyślić przypadki testowe dla tego wyzwania , Wybrałem 25 nagłówków z The Onion subreddit (gdzie publikowane są artykuły z The Onion i jego witryn podrzędnych, takich jak ClickHole) i 25 nagłówków z subreddit Not The Onion (gdzie publikowane są prawdziwe artykuły, które brzmią jak satyra). Jedyne zmiany, jakie wprowadziłem w nagłówkach, to zastąpienie „fantazyjnych” cytatów zwykłymi cudzysłowami ASCII i ujednolicenie wielkości liter – wszystko inne pozostaje niezmienione w stosunku do nagłówka oryginalnego artykułu. Każdy nagłówek znajduje się w osobnej linii.

Nagłówki cebuli

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 

Komentarze

  • Your score will be the number of correct outputs divided by the total number of headlines Czy bytecount to remis?
  • Ja ' trochę zdezorientowany. Jakiego rodzaju rozwiązania oczekujesz? Każde rozwiązanie będzie musiało zostać ” zoptymalizowane pod kątem przypadków testowych ” w pewnym sensie, zarzuć pisanie sztucznej inteligencji, która rozumie angielski i ma poczucie humoru. Na przykład rozwiązanie Arnaulda ' wykrywa /ly\b/ który działa tylko dlatego, że 25 Onio N nagłówki, które wybrałeś, mają więcej przysłówków, ale z tego co wiem, możesz łatwo potknąć się z inną baterią testową. A kto ' powiedział, że jego współczynniki nie są ' wybrane do optymalizacji wyniku? (Dlaczego nie ' nie optymalizowałby ich?)
  • Ta bateria testowa wydaje się nieco nietypowa. To ' to jak proszenie o klasyfikator, który może wykrywać psy na zdjęciu, ale biorąc pozytywne przypadki testowe jako zdjęcia psów i negatywne przypadki testowe z artykułu Buzzfeed pod tytułem ” 25 zdjęć obiektów, które ' Przysięgam, że są psami, ale nie, okazuje się, że są ' t! (# 11 rozwali ci umysł!) ” To sprawia, że wystarczająco trudny problem jest trudniejszy.
  • Nie tylko wyzwanie jest trudne, ale ' również nie jest (dla mnie) oczywiste, czym ' jest różnica. Jeśli ' nie mogę go rozwiązać, to oczywiście mój program nie może ' t go rozwiązać (to znaczy przekonać mnie, że tak nie jest) ' t stały kod przypadków testowych)
  • Cóż, spędziłem +36 godzin na szkoleniu sztucznej sieci neuronowej przy użyciu brain.js i LSTM, z przykładami w tym numerze i 100 innymi próbkami każdego typu z podanych linków, ale wynik nie był ' wystarczająco dobry z nowymi tytułami, które nie były ' nie występuje w zestawach treningowych. ' Gotowe: P

Odpowiedź

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

63,5% (127/200) na ukrytych przypadkach testowych

Prosta heurystyka oparta na długości tytułu, liczbie spacji i użyj sufiksu -ly.

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

Wypróbuj online!

Komentarze

  • To absurdalnie skuteczne, biorąc pod uwagę to, jak proste jest.
  • To rozwiązanie uzyskało 63,5% ukrytych przypadków testowych, więc jest poprawny.
  • Nie tak proste, jak było to możliwe na początku piaskownicy (100%, wykorzystując różnice w wielkości liter przed jej standaryzacją), ale to jest naprawdę proste.
  • @Mego Tak z ciekawości, czy ta wersja NSFW poprawia wynik ukrytych przypadków testowych? 🙂
  • @Arnauld 66% w tej wersji

Odpowiedz

Python 3, 84%

Nie sprawdzono w ukrytych przypadkach testowych.

Używa Keras LSTM RNN wyszkolonego na różnych nagłówkach. Aby go uruchomić, potrzebujesz następującego programu Keras i modelu, który udostępniłem na GitHub: łącze repozytorium . Będziesz potrzebował modelu .h5, a odwzorowania słów / wektorów znajdują się w .pkl. Najnowsze

Zależności są następujące:

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 

Dostępne ustawienia:

max_headline_length = 70 word_count = 20740 

Model to:

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

Teraz, aby załadować model i osadzanie słów:

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

Oraz kod do sprawdzenia, czy ciąg pochodzi z „NotTheOnion” lub „TheOnion „Napisałem” szybką funkcję pomocniczą, która konwertuje ciąg na odpowiednie osadzenie słów:

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 

Wyjaśnienie

Ten kod uruchamia model, który analizuje związki między słowami, przedstawiając słowa jako „wektor”. Więcej informacji na temat osadzania słów można znaleźć tutaj .

Jest to przeszkolone w nagłówkach, ale przypadki testowe są wykluczone .

Ten proces jest zautomatyzowany po dłuższej obróbce. Ostateczną listę przetworzonych słów rozprowadziłem jako .pkl, ale to, co dzieje się przy osadzaniu słów, polega na tym, że najpierw analizujemy zdanie i izolujemy słowa.

Teraz zapisz słowa Następnym krokiem jest zrozumienie różnic i podobieństw między określonymi słowami, np. king i queen i duke i duchess. Te osadzenia nie występują między słowami, ale między liczbami reprezentującymi słowa, które są przechowywane w .pkl plik. Słowa, których maszyna nie rozumie, są mapowane na specjalne słowo <UNK>, które pozwala nam zrozumieć, że jest tam słowo, ale nie wiadomo dokładnie, jakie jest jego znaczenie.

Teraz, gdy słowa są zrozumiałe, sekwencja słów (nagłówek) musi być analizowana. To właśnie robi „LSTM”, LTSM to typ komórki „RNN”, unika efektu znikającego gradientu. Mówiąc prościej, bierze sekwencję słów i pozwala nam znaleźć relacje między nimi.

Teraz ostatnia warstwa to Dense co zasadniczo oznacza, że jest to coś w rodzaju tablicy, co oznacza, że wynik wygląda następująco: [probability_is_not_onion, probability_is_onion]. Znajdując, który z nich jest większy, możemy wybrać, który z nich jest najbardziej pewny dla danego nagłówka.

Odpowiedź

Python 3 + Keras, 41/50 = 82%

83% (166/200) na ukrytych przypadkach testowych

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 i vocabulary.json można pobrać z tutaj (bardzo duży) i tutaj .

W pełni połączony klasyfikator połączony z wstępnie wytrenowanym modelem analizy sentymentów DeepMoji, który składa się z dwukierunkowych LSTM i mechanizmu uwagi. Zamroziłem warstwy DeepMoji i wyjąłem ostatnią warstwę softmax, wytrenowałem tylko w pełni połączone warstwy, a następnie odmroziłem warstwy DeepMoji i wytrenowałem je razem w celu dostrojenia. Mechanizm uwagi jest zaczerpnięty z https://github.com/bfelbo/DeepMoji/blob/master/deepmoji/attlayer.py (nie chciałem używać całego ich kodu jako zależności dla jednej klasy, zwłaszcza że jest to Python 2 i raczej nieporęczny w użyciu jako moduł …)

To działa zaskakująco słabo na zestawie testowym Mego, biorąc pod uwagę, że na moim własnym większym zestawie walidacyjnym osiąga> 90%. Więc jeszcze tego nie zrobiłem.

Komentarze

  • 83% w przypadku ukrytych przypadków testowych, zakładając, że uruchomiłem go poprawnie

Odpowiedź

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

96% (192/200) na ukrytych przypadkach testowych

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

Wymaga to dwóch dużych plików JSON, których nie mogę umieścić tutaj ani na ” TiO „. Pobierz je z poniższych linków i zapisz je z words.json i nazwy, w tym samym folderze co plik JS. Znajduje się tam również łącze do pliku JS z przypadkami testowymi i wydrukami wyników / procent.Możesz umieścić swoje ukryte przypadki testowe w zmiennych onions i nonOnions.

Po zapisaniu wszystkich 3 plików w tym samym katalogu uruchom node onion.js.

Funkcja O zwróci true, jeśli jest to cebula i false jeśli nie jest „t. Używa dużej listy worków słów (bez kolejności), aby wykryć, czy ciąg wejściowy to cebula. Rodzaj zakodowany na stałe, ale działa bardzo dobrze w różnych testach losowych przypadków.

Komentarze

  • To rozwiązanie daje 96% w przypadku ukrytych przypadków testowych.

Odpowiedź

Praca z rozwiązaniem Arnaulda

JavaScript (ES6), 41/50

64% (128/200) w przypadku ukrytych przypadki testowe

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) ukrytych przypadków testowych (nieprawidłowe)

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 

Długość + liczba słów + „ly „koncepcja działa całkiem nieźle, udało mi się wycisnąć jeszcze kilka punktów, sprawdzając słowo„ tato ”(kiedy prawdziwe artykuły mówią o ojcach ludzi w trzeciej osobie w tytule?) i dodatkową kwestię, zmieniając heurystyka wyszukiwania „ly” i sprawdzanie obecności liczb w tytule (co może być mniej ważne w ogólnym przypadku poza testem, więc zostawiłem oba rozwiązania)

Komentarze

  • Nie ' nie wiem o części dla taty … wydaje mi się, że trochę przypomina optymalizację przypadku testowego …
  • I tak, mogę znaleźć wiele artykułów Not the Onion wspominających o ojcach
  • Istnieje ' prawdopodobnie lepszy sposób na zrobienie tego w ramach heurystyki a nie tylko trudne ” win ” jeśli zawiera tatę, ale wyobrażam sobie, że nawet poza testową bazą danych abstrakcyjnie mówię o konkretnym ” Tato ” jest bardziej powszechne w The Onion
  • Twoje pierwsze rozwiązanie uzyskało 64% w przypadku ukrytych przypadków testowych, więc jest prawidłowe. Twoje drugie rozwiązanie uzyskało 62,5% w przypadku ukrytych przypadków testowych, więc nie jest poprawne.
  • @Mego Co za mały margines …

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *