Цибуля, чи не цибуля?


11

Лук (попередження: багато статей є NSFW) - сатирична новинна організація, яка пародіює традиційні ЗМІ. У 2014 році The Onion запустив ClickHole (попередження: також часто NSFW), веб-сайт із сатиричних новин, який пародіює "кліки" на сайтах типу BuzzFeed. Завдяки Закону По , люди досить часто читають заголовки статей із The Onion або ClickHole і вважають, що вони правдиві, не знаючи, що вони призначені для сатири. Зворотне буває і зі смішно звучали справжніми новинами - люди часто думають, що вони сатири, коли їх немає.

Ця плутанина, природно, піддається грі - враховуючи заголовок новин, спробуйте вгадати, чи це сатира чи ні. Ця проблема полягає в тому, щоб робити саме це з програмою.

Враховуючи заголовок новин (рядок, що складається лише з символів та пробілів для друку ASCII), виведіть, 1якщо заголовок сатиричний, або 0якщо його немає. Вашим балом буде кількість правильних результатів, поділене на загальну кількість заголовків.

Як правило, стандартні лазівки (особливо оптимізаційні для тестових випадків ) не допускаються. Для цього я запускаю ваші програми на наборі 200 прихованих тестових випадків (100 від The ​​Onion, 100 від Not The Onion). Ваше рішення має набрати не більше ніж 20 відсоткових балів менше, ніж ваша оцінка в громадських тестових випадках, щоб бути дійсною.

Випробування

Щоб придумати тестові приклади для цього виклику, я вибрав 25 заголовків із підредагу The Onion (де розміщуються статті з The Onion та його дочірніх сайтів, як-от ClickHole), та 25 заголовків з підвідкладу Not The Onion (де реальні статті новин які звучать як сатира). Єдині зміни, які я внесла до заголовків, були заміною "фантазійних" цитат звичайними котируваннями ASCII та стандартизацією написання великих літер - все інше залишається незмінним від заголовка оригінальної статті. Кожен заголовок має свій власний рядок.

Заголовки цибулі

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

Не заголовки цибулі

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 headlinesЄ двобічний переривник?
Skidsdev

9
Я трохи розгублений. Що вид розчину можна очікувати? Кожне рішення повинно буде дещо "оптимізувати для тестових випадків", бар написання AI, який може розуміти англійську мову і має сенс для гумору. Наприклад, рішення Arnauld виявляє, /ly\b/що працює лише тому, що 25 вибраних вами заголовків цибулі мають більше прислівників, але, наскільки я знаю, ви могли легко зв'язати його з іншим тестовим акумулятором. І хто скаже, що його коефіцієнти не вибрані для оптимізації його балів? (Чому б він не оптимізував їх?)
Лінн

10
Цей тестовий акумулятор здається трохи незвичним. Це як би просити класифікатора, який може виявити собак на фотографії, але беручи ваші позитивні тестові випадки як фотографії собак та ваші негативні тестові приклади із статті Buzzfeed під назвою "25 фотографій об'єктів, які ви будете лаятись собаками, але немає, повороти" Їх немає! (№11 підірве ваш розум!) "Це ускладнює досить важку проблему.
Софія Лехнер

4
Виклик не тільки важкий, але й не очевидно (для мене) в чому різниця. Якщо я не можу її вирішити, звичайно, моя програма не може її вирішити (тобто переконайте, що вона не є жорстким кодом для тестових випадків)
user202729

4
Добре я провів +36 годин на тренування штучної нейронної мережі з використанням brain.jsта LSTM, із зразками в цьому випуску та 100 іншими зразками кожного типу із наданих посилань, але результат виявився недостатньо хорошим із новими заголовками, яких не було у навчальних наборах . Я закінчую: P
Ніч2,

Відповіді:


7

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

63,5% (127/200) на приховані тестові справи

Простий евристичний на основі довжини заголовка, кількості пробілів та використання -lyсуфікса.

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

Спробуйте в Інтернеті!


Це абсурдно ефективно, наскільки це просто.
Дон Тисяча

Це рішення набрало 63,5% у прихованих тестових випадках, тому воно справедливо.
Мего

Не так просто, як це було можливо на початку пісочниці (100%, використовуючи різниці в капіталізації до того, як вона була стандартизована), але це дійсно просто.
Zacharý

@Mego Просто з цікавості, чи покращує ця версія NSFW бал за прихованими тестовими кейсами? :)
Арнольд

@Arnauld 66% з цією версією
Mego

6

Пітон 3, 84%

Не перевірено на прихованих тестових випадках.

Для цього використовується Keras LSTM RNN, підготовлений на різних заголовках. Для його запуску вам знадобиться Keras наступне та модель, яку я зробив доступною на GitHub: repo посилання . Вам знадобиться модель .h5і відображення слова / вектора .pkl. Останній

Залежності:

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

Налаштування:

max_headline_length = 70
word_count = 20740

Модель:

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

Тепер, щоб завантажити модель та вбудовування слова:

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

І код, щоб перевірити, чи є рядок з "NotTheOnion" або "TheOnion", я написав функцію швидкої допомоги, яка перетворює рядок у відповідні вбудовані слова:

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

Пояснення

Цей код працює за моделлю, яка аналізує зв'язки між словами, представляючи слова як "вектор". Ви можете дізнатися більше про вбудовування слів тут .

Ця підготовка проводиться за заголовками, але тестові випадки виключені .

Цей процес автоматизований після зовсім небагато обробки. Я розподілив остаточний оброблений список слів як, .pklале те, що відбувається при вбудовуванні слова, спочатку аналізуємо речення та виділяємо слова.

Після того, як ми тепер маємо слова наступний крок , щоб бути в змозі зрозуміти відмінності і подібності між певними словами , наприклад , kingі в queenпорівнянні dukeі duchess. Ці вбудовування відбуваються не між власне словами, а між числами, що представляють слова, які зберігаються у .pklфайлі. Слова, які машина не розуміє, відображаються на спеціальне слово, <UNK>яке дозволяє нам зрозуміти, що там є слово, але невідомо, що саме означає.

Тепер, коли слова вдається зрозуміти, послідовність слів (заголовок) потребує можливості аналізу. Це те, що робить 'LSTM', LTSM - це тип клітини 'RNN', який уникає зникаючого градієнтного ефекту. Простіше кажучи, він займає послідовність слів, і це дозволяє нам знаходити зв’язки між ними.

Тепер останній шар , Denseякий в основному означає , що це ніби як масив означає вихід, як: [probability_is_not_onion, probability_is_onion]. Виявивши, який з них більший, ми можемо вибрати, який із найбільш впевнених результатів для даного заголовка.


3

Python 3 + Keras, 41/50 = 82%

83% (166/200) на прихованих тестових випадках

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і vocabulary.jsonїх можна отримати звідси (дуже великі) та тут .

Повністю підключений класифікатор, підключений до заздалегідь підготовленої моделі аналізу настроїв DeepMoji, яка складається з складених двонаправлених LSTM та уважного механізму. Я заморозив шари DeepMoji і вийняв остаточний шар softmax, тренував тільки повністю з'єднані шари, потім розморожував шари DeepMoji і тренував їх разом для фінітюнінгу. Механізм уваги взято з https://github.com/bfelbo/DeepMoji/blob/master/deepmoji/attlayer.py (я не хотів використовувати весь їх код як залежність для одного класу, тим більше, що це Python 2 і досить непростий у використанні в якості модуля ...)

Це виходить напрочуд погано на тестовому наборі Мего, враховуючи, що для мого більшого набору валідації він отримує> 90%. Тому я з цим ще не закінчив.


83% на прихованих тестових випадках, якщо припустити, що я правильно його запустив
Мего,

1

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

96% (192/200) на прихованих тестових випадках

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

Для цього потрібні два великих файли JSON, які я не можу розмістити тут або на "TiO". Будь ласка , завантажте їх з наступних посилань і зберігати їх з words.jsonі bags.jsonіменами, в тій же папці, що і JS - файлу. Також є посилання на файл JS з тестовими кейсами та друком результатів / відсотків. Ви можете помістити свої приховані тестові випадки у onionsта nonOnionsзмінні.

Після збереження всіх 3 файлів у одному каталозі запустіть node onion.js.

OФункція буде повертати trueякщо цибуля і falseякщо це не так . Використовує великий список мішечків слів (без порядку), щоб визначити, чи вводиться рядок цибулі. Вид жорстко кодований, але дуже добре працює на різних випадкових тестових випадках.


Це рішення отримує 96% у прихованих тестових випадках
Mego

0

Опрацювання рішення Арнальда

JavaScript (ES6), 41/50

64% (128/200) на приховані тестові справи

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) на приховані тестові справи (недійсні)

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

Концепція "довжина + кількість слів" + "ly" працює досить добре, мені вдалося виділити ще кілька балів, перевіривши на слово "тато" (коли реальні статті говорять про татків людей у ​​третій особі в заголовку?) Та додатковий пункт, змінивши евристичний пошук "ly" та перевіривши наявність чисел у заголовку (що може бути менш правдивим у загальному випадку поза тестом, тому я залишив обидва рішення)


Я не знаю про татову частину ... мені здається трохи схожим на оптимізацію тестового випадку для мене ...
Дон Тисяча

І так, я можу знайти безліч статей Not Theion, в яких згадуються про тата
Дон Тисяча

Напевно, є кращий спосіб зробити це як частину евристичної, а не просто важкої "виграші", якщо вона містить тата, але я думаю, навіть поза тестовою базою даних абстрактно говорити про конкретного "тата", що частіше зустрічається в "Луці"
TiKevin83

Ваше перше рішення набрало 64% ​​на прихованих тестових випадках, тож воно дійсне. Ваше друге рішення набрало 62,5% у прихованих тестових випадках, тому воно недійсне.
Мего

@Mego Що за межами поля ...
користувач202729
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.