¿La cebolla o no la cebolla?

11

The Onion (advertencia: muchos artículos son NSFW) es una organización de noticias satírica que parodia los medios de comunicación tradicionales. En 2014, The Onion lanzó ClickHole (advertencia: también con frecuencia NSFW), un sitio web de noticias satíricas que parodia sitios de "clickbait" como BuzzFeed. Gracias a la Ley de Poe , es bastante común que las personas lean los titulares de los artículos de The Onion o ClickHole y crean que son ciertos, sin saber que están destinados a ser una sátira. Lo contrario también ocurre con historias de noticias reales que suenan ridículas: las personas a menudo piensan que son sátiras cuando no lo son.

Esta confusión se presta naturalmente a un juego: dado un titular de noticias, intenta adivinar si es o no una sátira. Este desafío se trata de hacer exactamente eso con un programa.

Dado un titular de noticias (una cadena que consta de solo caracteres y espacios ASCII imprimibles), se 1muestra si el titular es una sátira o 0si no lo es. Su puntaje será el número de resultados correctos dividido por el número total de titulares.

Como es habitual, no se permiten las lagunas estándar (especialmente la optimización para los casos de prueba ). Para hacer cumplir esto, ejecutaré sus programas en un conjunto de 200 casos de prueba ocultos (100 de The Onion, 100 de Not The Onion). Para que sea válida, su solución no debe tener más de 20 puntos porcentuales menos que su puntaje en los casos de prueba públicos.

Casos de prueba

Para encontrar casos de prueba para este desafío, elegí 25 titulares del subreddit de The Onion (donde se publican artículos de The Onion y sus sitios secundarios, como ClickHole), y 25 titulares del subreddit Not The Onion (donde artículos de noticias reales que suena como sátira se publican). Los únicos cambios que hice en los titulares fueron reemplazar las citas "elegantes" con citas ASCII regulares y estandarizar la capitalización; todo lo demás se mantiene sin cambios desde el título del artículo original. Cada título está en su propia línea.

Los titulares de cebolla

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

No los titulares de cebolla

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
Mego
fuente
66
Your score will be the number of correct outputs divided by the total number of headlines¿Bytecount es un desempate?
Skidsdev
99
Estoy un poco confundido. ¿Qué tipo de solución esperas? Cada solución tendrá que "optimizarse para los casos de prueba", escribir una IA que entienda el inglés y tenga sentido del humor. Por ejemplo, la solución de Arnauld detecta /ly\b/cuál funciona solo porque los 25 titulares de Cebolla que elegiste tienen más adverbios, pero por lo que sé, podrías tropezarlo fácilmente con una batería de prueba diferente. ¿Y quién puede decir que sus coeficientes no se eligen para optimizar su puntaje? (¿Por qué no los optimizaría?)
Lynn
10
Esta batería de prueba parece un poco inusual. Es como pedir un clasificador que pueda detectar perros en una fotografía, pero tomar sus casos de prueba positivos como fotos de perros y sus casos de prueba negativos de un artículo de Buzzfeed titulado "25 fotos de objetos que jurará que son perros, pero no, vueltas ¡Fuera no lo están! (¡# 11 te dejará boquiabierto!) "Hace que un problema sea bastante difícil.
Sophia Lechner
44
El desafío no solo es difícil, sino que tampoco es obvio (para mí) cuál es la diferencia. Si no puedo resolverlo, por supuesto, mi programa no puede resolverlo (es decir, mientras me convence de que no codifica para los casos de prueba)
user202729
44
Bueno, pasé +36 horas entrenando una red neuronal artificial usando brain.jsLSTM, con muestras en este número y otras 100 muestras de cada tipo de los enlaces provistos, pero el resultado no fue lo suficientemente bueno con nuevos títulos que no estaban presentes en los conjuntos de entrenamiento . Ya
terminé

Respuestas:

7

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

63.5% (127/200) en casos de prueba ocultos

Una heurística simple basada en la longitud del título, el número de espacios y el uso del -lysufijo.

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

Pruébalo en línea!

Arnauld
fuente
Esto es absurdamente efectivo por lo simple que es.
Don Thousand
Esta solución obtuvo un 63.5% en los casos de prueba ocultos, por lo que es válida.
Mego
No es tan simple como era posible al comienzo del sandbox (100%, utilizando las diferencias de capitalización antes de que fuera estandarizado), pero esto es realmente simple.
Zacharý
@Mego Solo por curiosidad, ¿esta versión NSFW mejora la puntuación en los casos de prueba ocultos? :)
Arnauld
@Arnauld 66% con esa versión
Mego
6

Python 3, 84%

No probado en casos de prueba ocultos.

Utiliza Keras LSTM RNN entrenado en varios titulares. Para ejecutarlo, necesita Keras lo siguiente y el modelo que he puesto a disposición en GitHub: enlace repo . Necesitará el modelo .h5y las asignaciones de palabras / vectores están en .pkl. Lo último

Las dependencias son:

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

Las configuraciones son:

max_headline_length = 70
word_count = 20740

El modelo es:

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

Ahora para cargar el modelo y las incrustaciones de palabras:

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

Y el código para probar si una cadena es de 'NotTheOnion' o 'TheOnion' He escrito una función de ayuda rápida que convierte la cadena a las incrustaciones de palabras respectivas:

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

Explicación

Este código ejecuta un modelo que analiza las relaciones entre palabras al representar las palabras como un 'vector'. Puede obtener más información sobre la incrustación de palabras aquí .

Esto está entrenado en los titulares, pero los casos de prueba están excluidos .

Este proceso se automatiza después de un poco de procesamiento. He distribuido la lista de palabras procesadas final como una, .pklpero lo que sucede en la inclusión de palabras es que primero analizamos la oración y aislamos las palabras.

Después de que tengamos las palabras, el siguiente paso es ser capaz de comprender las diferencias y similitudes entre ciertas palabras, por ejemplo, kingy queenversus dukey duchess. Estas incrustaciones no ocurren entre las palabras reales, sino entre números que representan las palabras, que es lo que se almacena en el .pklarchivo. Las palabras que la máquina no comprende se asignan a una palabra especial <UNK>que nos permite comprender que hay una palabra allí pero que no se sabe exactamente cuál es el significado.

Ahora que las palabras se pueden entender, la secuencia de palabras (título) debe poder analizarse. Esto es lo que hace 'LSTM', un LTSM es un tipo de célula 'RNN' que evita el efecto de gradiente de fuga. Más simplemente, toma una secuencia de palabras y nos permite encontrar relaciones entre ellas.

Ahora la capa final es Denselo que básicamente significa que es algo así como una matriz que significa la salida es como: [probability_is_not_onion, probability_is_onion]. Al encontrar cuál es más grande, podemos elegir cuál es el resultado más seguro para el titular dado.

Downgoat
fuente
3

Python 3 + Keras, 41/50 = 82%

83% (166/200) en casos de prueba ocultos

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.h5y vocabulary.jsonse puede recuperar desde aquí (muy grande) y aquí .

Clasificador totalmente conectado conectado a un modelo de análisis de sentimientos pre-entrenado DeepMoji, que consiste en LSTM bidireccionales apiladas y un mecanismo de atención. Congelé las capas DeepMoji y saqué la capa softmax final, entrené solo las capas completamente conectadas, luego descongelé las capas DeepMoji y las entrené juntas para un ajuste fino. El mecanismo de atención se toma de https://github.com/bfelbo/DeepMoji/blob/master/deepmoji/attlayer.py (no quería tener que usar todo su código como dependencia para una clase, especialmente porque es Python 2 y bastante difícil de usar como módulo ...)

Esto funciona sorprendentemente mal en el conjunto de prueba de Mego, considerando que en mi propio conjunto de validación más grande obtiene> 90%. Así que aún no he terminado con esto.

un espagueti
fuente
83% en casos de prueba ocultos, suponiendo que lo ejecuté correctamente
Mego
1

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

96% (192/200) en casos de prueba ocultos

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

Esto requiere dos grandes archivos JSON que no puedo poner aquí o en "TiO". Descárguelos de los siguientes enlaces y guárdelos con los nombres words.jsony bags.json, en la misma carpeta que el archivo JS. También hay un enlace para un archivo JS con casos de prueba e impresión de resultados / porcentaje. Puede poner sus casos de prueba ocultos en onionsy nonOnionsvariables.

Después de guardar los 3 archivos en el mismo directorio, ejecute node onion.js.

La Ofunción volverá truesi es cebolla y falsesi no lo es. Utiliza una gran lista de bolsas de palabras (sin orden) para detectar si la cadena de entrada es cebolla. Es un poco codificado, pero funciona muy bien en una variedad de casos de prueba aleatorios.

Noche2
fuente
Esta solución obtiene el 96% en los casos de prueba ocultos
Mego
0

Trabajando con la solución de Arnauld

JavaScript (ES6), 41/50

64% (128/200) en casos de prueba ocultos

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) en casos de prueba ocultos (inválido)

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

El concepto de longitud + recuento de palabras + "ly" funciona bastante bien, pude exprimir algunos puntos más al buscar la palabra "Papá" (¿cuándo los artículos reales hablan de los padres de las personas en la tercera persona del título?) Y un punto adicional al cambiar la heurística de búsqueda "ly" y verificar la presencia de números en el título (que podría ser menos válido en el caso general fuera de la prueba, así que dejé ambas soluciones)

TiKevin83
fuente
No sé sobre la parte de papá ... me parece un poco como optimizar el caso de prueba ...
Don Thousand
Y sí, puedo encontrar muchos artículos de Not the Onion que mencionan a los padres
Don Thousand, el
Probablemente haya una mejor manera de hacerlo como parte de la heurística y no solo una "victoria" difícil si contiene a papá, pero imagino que incluso fuera de la base de datos de prueba, hablar de forma abstracta sobre un "papá" específico es más común en The Onion
TiKevin83
Su primera solución obtuvo un 64% en los casos de prueba ocultos, por lo que es válida. Su segunda solución obtuvo un 62.5% en los casos de prueba ocultos, por lo que no es válida.
Mego
@Mego Qué margen de cierre ...
user202729