L'oignon ou pas l'oignon?


11

The Onion (avertissement: de nombreux articles sont NSFW) est un organisme de presse satirique qui parodie les médias d'information traditionnels. En 2014, The Onion a lancé ClickHole (avertissement: également fréquemment NSFW), un site d'informations satiriques qui parodie les sites "clickbait" comme BuzzFeed. Grâce à la loi de Poe , il est assez courant pour les gens de lire les gros titres d'articles de The Onion ou ClickHole et de les croire vrais, sans savoir qu'ils sont destinés à être de la satire. L'inverse se produit également avec de vraies nouvelles ridicules - les gens pensent souvent qu'ils sont de la satire alors qu'ils ne le sont pas.

Cette confusion se prête naturellement à un jeu - étant donné un gros titre, essayez de deviner s'il s'agit ou non d'une satire. Ce défi consiste à faire exactement cela avec un programme.

Étant donné un titre d'actualités (une chaîne composée uniquement de caractères et d'espaces ASCII imprimables), affichez 1si le titre est satirique ou 0non. Votre score sera le nombre de sorties correctes divisé par le nombre total de titres.

Comme d'habitude, les failles standard (en particulier l' optimisation pour les cas de test ) ne sont pas autorisées. Pour appliquer cela, je vais exécuter vos programmes sur un ensemble de 200 cas de test cachés (100 de The Onion, 100 de Not The Onion). Pour être valide, votre solution ne doit pas marquer plus de 20 points de pourcentage de moins que votre score sur les cas de test publics.

Cas de test

Pour trouver des cas de test pour ce défi, j'ai choisi 25 titres du subreddit The Onion (où les articles de The Onion et ses sites enfants, comme ClickHole, sont publiés), et 25 titres du subreddit Not The Onion (où de vrais articles de presse qui sonnent comme de la satire sont affichés). Les seules modifications que j'ai apportées aux titres ont été de remplacer les guillemets «fantaisistes» par des guillemets ASCII réguliers et de normaliser la capitalisation - tout le reste est inchangé par rapport au titre de l'article d'origine. Chaque titre est sur sa propre ligne.

Les gros titres de l'oignon

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

Pas les gros titres

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 est-il un casse-cravate?
Skidsdev

9
Je suis un peu confuse. À quel type de solution vous attendez-vous? Chaque solution devra quelque peu "optimiser pour les cas de test", à l'exception de l'écriture d'une IA qui peut comprendre l'anglais et qui a le sens de l'humour. Par exemple, la solution d'Arnauld détecte /ly\b/ce qui fonctionne uniquement parce que les 25 titres Oignon que vous avez choisis se trouvent avoir plus d'adverbes, mais pour autant que je sache, vous pouvez facilement le déclencher avec une batterie de test différente. Et qui peut dire que ses coefficients ne sont pas choisis pour optimiser son score? (Pourquoi ne les optimiserait-il pas?)
Lynn

10
Cette batterie de test semble un peu inhabituelle. C'est comme demander un classificateur capable de détecter les chiens sur une photo, mais prendre vos cas de test positifs comme des photos de chiens et vos cas de test négatifs à partir d'un article de Buzzfeed intitulé "25 photos d'objets que vous jurez sont des chiens, mais non, tourne Ils ne le sont pas! (# 11 Will Blow Your Mind!) "Cela rend un problème assez difficile encore plus difficile.
Sophia Lechner

4
Non seulement le défi est difficile, mais il n'est pas évident (pour moi) quelle est la différence. Si je ne peux pas le résoudre, bien sûr mon programme ne peut pas le résoudre (c'est-à-dire, tout en me convaincant qu'il ne code pas en dur pour les cas de test)
user202729

4
Eh bien, j'ai passé +36 heures à former un réseau de neurones artificiels à l'aide de brain.jset LSTM, avec des échantillons dans ce numéro et 100 autres échantillons de chaque type à partir des liens fournis, mais le résultat n'était pas assez bon avec de nouveaux titres qui n'étaient pas présents dans les ensembles de formation . J'ai fini: P
Night2

Réponses:


7

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

63,5% (127/200) sur les cas de test cachés

Une heuristique simple basée sur la longueur du titre, le nombre d'espaces et l'utilisation du -lysuffixe.

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

Essayez-le en ligne!


C'est absurdement efficace pour sa simplicité.
Don Thousand

Cette solution a obtenu 63,5% sur les cas de test cachés, elle est donc valide.
Mego

Pas aussi simple que cela était possible au début du bac à sable (100%, en utilisant les différences de capitalisation avant qu'il ne soit standardisé), mais c'est vraiment simple.
Zacharý

@Mego Juste par curiosité, cette version NSFW améliore-t-elle le score sur les cas de test cachés? :)
Arnauld

@Arnauld 66% avec cette version
Mego

6

Python 3, 84%

Non testé sur les cas de test cachés.

Cela utilise Keras LSTM RNN formé sur divers titres. Pour l'exécuter, vous avez besoin de Keras et du modèle que j'ai mis à disposition sur GitHub: repo link . Vous aurez besoin du modèle .h5et des mappages de mots / vecteurs .pkl. Le dernier

Les dépendances sont:

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

Les paramètres sont les suivants:

max_headline_length = 70
word_count = 20740

Le modèle est:

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

Maintenant, pour charger le modèle et le mot incorporations:

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

Et le code pour tester si une chaîne provient de «NotTheOnion» ou «TheOnion», j'ai écrit une fonction d'aide rapide qui convertit la chaîne en incorporations de mots respectives:

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

Explication

Ce code exécute un modèle qui analyse les relations entre les mots en représentant les mots comme un «vecteur». Vous pouvez en savoir plus sur l'intégration de mots ici .

Celui-ci est formé aux titres mais les cas de test sont exclus .

Ce processus est automatisé après un certain temps de traitement. J'ai distribué la liste finale de mots traités en tant que .pklmais ce qui se passe dans l'intégration de mots est d'abord d'analyser la phrase et d'isoler les mots.

Une fois que nous avons maintenant les mots, la prochaine étape consiste à être en mesure de comprendre les différences et les similitudes entre certains mots, par exemple kinget par queenrapport à dukeet duchess. Ces incorporations ne se produisent pas entre les mots réels mais entre des nombres représentant les mots, ce qui est ce qui est stocké dans le .pklfichier. Les mots que la machine ne comprend pas sont mappés sur un mot spécial <UNK>qui nous permet de comprendre qu'il y a un mot là-bas mais que l'on ne sait pas exactement ce que c'est.

Maintenant que les mots peuvent être compris, la séquence de mots (titre) doit pouvoir être analysée. C'est ce que fait «LSTM», un LTSM est un type de cellule «RNN» qui évite l'effet de gradient de fuite. Plus simplement, cela prend une séquence de mots et cela nous permet de trouver des relations entre eux.

Maintenant , la dernière couche est ce Densequi signifie essentiellement c'est un peu comme un tableau qui signifie que la sortie est comme: [probability_is_not_onion, probability_is_onion]. En trouvant lequel est le plus grand, nous pouvons choisir celui qui est le résultat le plus sûr pour le titre donné.


3

Python 3 + Keras, 41/50 = 82%

83% (166/200) sur les cas de test cachés

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.h5et vocabulary.jsonpeut être récupéré d' ici (très grand) et ici .

Classificateur entièrement connecté connecté à un modèle d'analyse des sentiments pré-formé DeepMoji, qui se compose de LSTM bidirectionnels empilés et d'un mécanisme attentionnel. J'ai gelé les couches DeepMoji et retiré la couche finale softmax, formé uniquement les couches entièrement connectées, puis dégelé les couches DeepMoji et les ai entraînées ensemble pour le réglage fin. Le mécanisme attentionnel est tiré de https://github.com/bfelbo/DeepMoji/blob/master/deepmoji/attlayer.py (je ne voulais pas avoir à utiliser tout leur code comme dépendance pour une classe, d'autant plus que c'est Python 2 et plutôt lourd à utiliser comme module ...)

Cela fonctionne étonnamment mal sur l'ensemble de test de Mego, étant donné que sur mon propre ensemble de validation plus grand, il obtient> 90%. Je n'ai donc pas encore fini avec ça.


83% sur les cas de test cachés, en supposant que je l'ai exécuté correctement
Mego

1

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

96% (192/200) sur les cas de test cachés

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

Cela nécessite deux gros fichiers JSON que je ne peux pas les mettre ici ou sur "TiO". S'il vous plaît télécharger à partir des liens suivants et les enregistrer avec les words.jsonet bags.jsonnoms, dans le même dossier que le fichier JS. Il existe également un lien vers un fichier JS avec des cas de test et une impression résultat / pourcentage. Vous pouvez mettre vos cas de test cachés dans onionset nonOnionsvariables.

Après avoir enregistré les 3 fichiers dans le même répertoire, exécutez node onion.js.

La Ofonction retournera truesi c'est de l'oignon et falsesi ce n'est pas le cas. Utilise une grande liste de sacs de mots (sans ordre) pour détecter si la chaîne d'entrée est oignon. Type de code dur, mais fonctionne très bien sur une variété de cas de test aléatoires.


Cette solution obtient 96% sur les cas de test cachés
Mego

0

Travailler avec la solution d'Arnauld

JavaScript (ES6), 41/50

64% (128/200) sur les cas de test cachés

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) sur les cas de test cachés (invalide)

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

Le concept de longueur + nombre de mots + "ly" fonctionne plutôt bien, j'ai pu tirer quelques points supplémentaires en vérifiant le mot "papa" (quand les vrais articles parlent-ils des papas des gens à la troisième personne du titre?) Et un point supplémentaire en changeant l'heuristique de recherche "ly" et en vérifiant la présence de nombres dans le titre (qui pourrait être moins valable dans le cas général en dehors du test, j'ai donc laissé les deux solutions)


Je ne sais pas pour la partie papa ... cela me semble un peu comme optimiser le cas de test ...
Don Thousand

Et oui, je peux trouver plein d'articles sur Not the Onion mentionnant les papas
Don Thousand

Il y a probablement une meilleure façon de le faire dans le cadre de l'heuristique et pas seulement une «victoire» difficile si elle contient papa, mais j'imagine que même en dehors de la base de données de test, parler de manière abstraite d'un «papa» spécifique est plus courant sur The Onion
TiKevin83

Votre première solution a obtenu 64% sur les cas de test masqués, elle est donc valide. Votre deuxième solution a obtenu 62,5% sur les cas de test masqués, elle n'est donc pas valide.
Mego

@Mego Quelle marge étroite ...
user202729
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.