Эта статья была рецензирована Верной Анчетой . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!
В последнее время, кажется, все и их пресловутая бабушка говорят о машинном обучении. Ваши социальные сети наполнены сообщениями о ML, Python, TensorFlow, Spark, Scala, Go и т. Д .; и если вы чем-то похожи на меня, вы можете спросить, а как насчет PHP?
Да, а как насчет машинного обучения и PHP? К счастью, кто-то был достаточно сумасшедшим, чтобы не только задать этот вопрос, но и разработать общую библиотеку машинного обучения, которую мы сможем использовать в нашем следующем проекте. В этой статье мы рассмотрим PHP-ML — библиотеку машинного обучения для PHP — и напишем класс для анализа настроений, который позже мы сможем использовать для нашего чата или твита-бота. Основными целями этого поста являются:
- Изучите общие понятия, касающиеся машинного обучения и анализа настроений
- Ознакомьтесь с возможностями и недостатками PHP-ML
- Определите проблему, над которой мы будем работать
- Докажите, что попытка сделать машинное обучение в PHP не является абсолютно безумной целью (необязательно)
Что такое машинное обучение?
Машинное обучение — это подмножество искусственного интеллекта, которое фокусируется на предоставлении «компьютерам возможности учиться без явного программирования». Это достигается с помощью универсальных алгоритмов, которые могут «учиться» из определенного набора данных.
Например, одним из распространенных применений машинного обучения является классификация. Алгоритмы классификации используются для помещения данных в разные группы или категории. Некоторые примеры приложений классификации:
- Фильтры электронной почты от спама
- Сегментация рынка
- Обнаружение мошенничества
Машинное обучение — это что-то вроде общего термина, который охватывает множество общих алгоритмов для различных задач, и существует два основных типа алгоритмов, классифицированных по способу обучения — контролируемое обучение и неконтролируемое обучение.
Контролируемое обучение
В контролируемом обучении мы обучаем наш алгоритм, используя помеченные данные в форме входного объекта (вектора) и желаемого выходного значения; алгоритм анализирует данные обучения и выдает то, что называется выведенной функцией, которую мы можем применить к новому, немаркированному набору данных.
В оставшейся части этого поста мы сосредоточимся на контролируемом обучении, просто потому, что его легче увидеть и проверить отношения; имейте в виду, что оба алгоритма одинаково важны и интересны; Можно утверждать, что неконтролируемый является более полезным, поскольку он исключает помеченные требования к данным.
Обучение без учителя
Этот тип обучения, с другой стороны, работает с немаркированными данными с самого начала. Мы не знаем желаемых выходных значений набора данных, и мы позволяем алгоритму делать выводы из наборов данных; неконтролируемое обучение особенно удобно при проведении поискового анализа данных для поиска скрытых закономерностей в данных.
PHP-ML
Познакомьтесь с PHP-ML, библиотекой, которая претендует на новый подход к машинному обучению в PHP. Библиотека реализует алгоритмы, нейронные сети и инструменты для предварительной обработки данных, перекрестной проверки и извлечения признаков.
Я буду первым, кто признает, что PHP — необычный выбор для машинного обучения, поскольку сильные стороны языка не очень хорошо подходят для приложений машинного обучения. Тем не менее, не каждому приложению для машинного обучения требуется обрабатывать петабайты данных и выполнять массивные вычисления — для простых приложений мы должны иметь возможность обойтись без использования PHP и PHP-ML.
Лучший вариант использования, который я сейчас вижу для этой библиотеки, — это реализация классификатора, будь то спам-фильтр или даже анализ настроений. Мы собираемся определить проблему классификации и шаг за шагом построить решение, чтобы увидеть, как мы можем использовать PHP-ML в наших проектах.
Проблема
Чтобы проиллюстрировать процесс реализации PHP-ML и добавления некоторого машинного обучения в наши приложения, я хотел найти забавную проблему для решения и какой лучший способ продемонстрировать классификатор, чем создание класса анализа настроений в твиттере.
Одним из ключевых требований, необходимых для создания успешных проектов машинного обучения, является достойный начальный набор данных. Наборы данных имеют решающее значение, поскольку они позволят нам обучить наш классификатор на основе уже классифицированных примеров. Поскольку в последнее время в средствах массовой информации вокруг авиакомпаний наблюдается значительный шум, какой набор данных лучше использовать, чем твиты от клиентов к авиакомпаниям?
К счастью, набор данных твитов уже доступен для нас благодаря Kaggle.io . С помощью этой ссылки можно загрузить базу данных Twitter Airline Sentiment со своего сайта.
Решение
Давайте начнем с обзора набора данных, над которым мы будем работать. Набор необработанных данных имеет следующие столбцы:
- tweet_id
- airline_sentiment
- airline_sentiment_confidence
- negativereason
- negativereason_confidence
- авиакомпания
- airline_sentiment_gold
- имя
- negativereason_gold
- retweet_count
- текст
- tweet_coord
- tweet_created
- tweet_location
- user_timezone
И выглядит как следующий пример (таблица с боковой прокруткой):
tweet_id | airline_sentiment | airline_sentiment_confidence | negativereason | negativereason_confidence | авиакомпания | airline_sentiment_gold | имя | negativereason_gold | retweet_count | текст | tweet_coord | tweet_created | tweet_location | user_timezone | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
570306133677760513 | нейтральный | 1,0 | Девственница америка | cairdin | 0 | @VirginAmerica Что @dhepburn сказал. | 2015-02-24 11:35:52 -0800 | Восточное время (США и Канада) | |||||||
570301130888122368 | положительный | 0,3486 | 0.0 | Девственница америка | jnardino | 0 | @VirginAmerica плюс вы добавили рекламу к опыту … безвкусно. | 2015-02-24 11:15:59 -0800 | Тихоокеанское время (США и Канада) | ||||||
570301083672813571 | нейтральный | 0,6837 | Девственница америка | yvonnalynn | 0 | @VirginAmerica Я не сделал сегодня … Должно быть, мне нужно отправиться в другое путешествие! | 2015-02-24 11:15:48 -0800 | Поиграем | Центральное время (США и Канада) | ||||||
570301031407624196 | отрицательный | 1,0 | Bad Flight | 0,7033 | Девственница америка | jnardino | 0 | «@VirginAmerica — это действительно агрессивно — взрывать отвратительные« развлечения »на лицах ваших гостей, и у них мало средств» | 2015-02-24 11:15:36 -0800 | Тихоокеанское время (США и Канада) | |||||
570300817074462722 | отрицательный | 1,0 | Не могу сказать | 1,0 | Девственница америка | jnardino | 0 | @VirginAmerica, и это действительно очень плохо | 2015-02-24 11:14:45 -0800 | Тихоокеанское время (США и Канада) | |||||
570300767074181121 | отрицательный | 1,0 | Не могу сказать | 0,6842 | Девственница америка | jnardino | 0 | «@VirginAmerica серьезно заплатил бы 30 долларов за полет за места, где не было этой игры. | |||||||
это действительно единственная плохая вещь в полете VA » | 2015-02-24 11:14:33 -0800 | Тихоокеанское время (США и Канада) | |||||||||||||
570300616901320704 | положительный | 0,6745 | 0.0 | Девственница америка | cjmcginnis | 0 | «@VirginAmerica да | почти каждый раз, когда я летаю на VX, этот «ушной червь» не исчезнет ? » | 2015-02-24 11:13:57 -0800 | Сан-Франциско, Калифорния | Тихоокеанское время (США и Канада) | ||||
570300248553349120 | нейтральный | 0,634 | Девственница америка | пилот | 0 | «@VirginAmerica Действительно упустила отличную возможность для пародии« Мужчины без шляп » | там. https://t.co/mWpG7grEZP» | 2015-02-24 11:12:29 -0800 | Лос-Анджелес | Тихоокеанское время (США и Канада) |
Файл содержит 14 640 твитов, так что для нас это неплохой набор данных. Теперь, с текущим количеством доступных столбцов, у нас гораздо больше данных, чем нужно для нашего примера; для практических целей мы заботимся только о следующих столбцах:
- текст
- airline_sentiment
Где text
airline_sentiment
Остальные столбцы могут быть отброшены, поскольку они не будут использоваться для нашего упражнения. Давайте начнем с создания проекта и инициализируем composer, используя следующий файл:
{
"name": "amacgregor/phpml-exercise",
"description": "Example implementation of a Tweet sentiment analysis with PHP-ML",
"type": "project",
"require": {
"php-ai/php-ml": "^0.4.1"
},
"license": "Apache License 2.0",
"authors": [
{
"name": "Allan MacGregor",
"email": "[email protected]"
}
],
"autoload": {
"psr-4": {"PhpmlExercise\\": "src/"}
},
"minimum-stability": "dev"
}
Если вам нужно введение в Composer, смотрите здесь .
Чтобы убедиться, что мы настроены правильно, давайте создадим быстрый скрипт, который загрузит наш composer install
Tweets.csv
Скопируйте следующий код как reviewDataset.php
<?php
namespace PhpmlExercise;
require __DIR__ . '/vendor/autoload.php';
use Phpml\Dataset\CsvDataset;
$dataset = new CsvDataset('datasets/raw/Tweets.csv',1);
foreach ($dataset->getSamples() as $sample) {
print_r($sample);
}
Теперь запустите скрипт с помощью php reviewDataset.php
Array( [0] => 569587371693355008 )
Array( [0] => 569587242672398336 )
Array( [0] => 569587188687634433 )
Array( [0] => 569587140490866689 )
Теперь это не выглядит полезным, не так ли? Давайте посмотрим на класс CsvDataset
<?php
public function __construct(string $filepath, int $features, bool $headingRow = true)
{
if (!file_exists($filepath)) {
throw FileException::missingFile(basename($filepath));
}
if (false === $handle = fopen($filepath, 'rb')) {
throw FileException::cantOpenFile(basename($filepath));
}
if ($headingRow) {
$data = fgetcsv($handle, 1000, ',');
$this->columnNames = array_slice($data, 0, $features);
} else {
$this->columnNames = range(0, $features - 1);
}
while (($data = fgetcsv($handle, 1000, ',')) !== false) {
$this->samples[] = array_slice($data, 0, $features);
$this->targets[] = $data[$features];
}
fclose($handle);
}
Конструктор CsvDataset
- Путь к файлу к исходному CSV
- Целое число, которое указывает количество объектов в нашем файле
- Логическое значение, указывающее, является ли первая строка заголовком
Если мы посмотрим немного поближе, то увидим, что класс отображает файл CSV на два внутренних массива: образцы и цели. Образцы содержат все функции, предоставляемые файлом, а цели содержат известные значения (отрицательные, положительные или нейтральные).
Исходя из вышеизложенного, мы видим, что формат, которому должен следовать наш CSV-файл, следующий:
| feature_1 | feature_2 | feature_n | target |
Нам нужно будет создать чистый набор данных только с теми столбцами, которые нам нужны для продолжения работы. Давайте назовем этот скрипт generateCleanDataset.php
<?php
namespace PhpmlExercise;
require __DIR__ . '/vendor/autoload.php';
use Phpml\Exception\FileException;
$sourceFilepath = __DIR__ . '/datasets/raw/Tweets.csv';
$destinationFilepath = __DIR__ . '/datasets/clean_tweets.csv';
$rows =[];
$rows = getRows($sourceFilepath, $rows);
writeRows($destinationFilepath, $rows);
/**
* @param $filepath
* @param $rows
* @return array
*/
function getRows($filepath, $rows)
{
$handle = checkFilePermissions($filepath);
while (($data = fgetcsv($handle, 1000, ',')) !== false) {
$rows[] = [$data[10], $data[1]];
}
fclose($handle);
return $rows;
}
/**
* @param $filepath
* @param string $mode
* @return bool|resource
* @throws FileException
*/
function checkFilePermissions($filepath, $mode = 'rb')
{
if (!file_exists($filepath)) {
throw FileException::missingFile(basename($filepath));
}
if (false === $handle = fopen($filepath, $mode)) {
throw FileException::cantOpenFile(basename($filepath));
}
return $handle;
}
/**
* @param $filepath
* @param $rows
* @internal param $list
*/
function writeRows($filepath, $rows)
{
$handle = checkFilePermissions($filepath, 'wb');
foreach ($rows as $row) {
fputcsv($handle, $row);
}
fclose($handle);
}
Ничего сложного, просто достаточно, чтобы сделать работу. Давайте выполним это с phpgenerateCleanDataset.php
Теперь давайте продолжим и укажем наш скрипт reviewDataset.php
Array
(
[0] => @AmericanAir That will be the third time I have been called by 800-433-7300 an hung on before anyone speaks. What do I do now???
)
Array
(
[0] => @AmericanAir How clueless is AA. Been waiting to hear for 2.5 weeks about a refund from a Cancelled Flightled flight & been on hold now for 1hr 49min
)
BAM! Это данные, с которыми мы можем работать! До сих пор мы создавали простые сценарии для манипулирования данными. Далее мы собираемся начать создание нового класса в src/classification/SentimentAnalysis.php
<?php
namespace PhpmlExercise\Classification;
/**
* Class SentimentAnalysis
* @package PhpmlExercise\Classification
*/
class SentimentAnalysis {
public function train() {}
public function predict() {}
}
Наш класс настроений будет нуждаться в двух функциях в нашем классе анализа настроений:
- Функция поезда , которая будет использовать наши обучающие образцы и метки набора данных и некоторые дополнительные параметры.
- Функция прогнозирования , которая будет принимать немаркированный набор данных и назначать набор меток на основе данных обучения.
В корне проекта создайте скрипт с именем classifyTweets.php
Мы будем использовать его сценарий для создания экземпляра и тестирования нашего класса анализа настроений. Вот шаблон, который мы будем использовать:
<?php
namespace PhpmlExercise;
use PhpmlExercise\Classification\SentimentAnalysis;
require __DIR__ . '/vendor/autoload.php';
// Step 1: Load the Dataset
// Step 2: Prepare the Dataset
// Step 3: Generate the training/testing Dataset
// Step 4: Train the classifier
// Step 5: Test the classifier accuracy
Шаг 1. Загрузите набор данных.
У нас уже есть базовый код, который мы можем использовать для загрузки CSV в объект набора данных из наших предыдущих примеров. Мы собираемся использовать тот же код с несколькими изменениями:
<?php
...
use Phpml\Dataset\CsvDataset;
...
$dataset = new CsvDataset('datasets/clean_tweets.csv',1);
$samples = [];
foreach ($dataset->getSamples() as $sample) {
$samples[] = $sample[0];
}
Это генерирует плоский массив только с функциями — в данном случае с текстом твита — который мы собираемся использовать для обучения нашего классификатора.
Шаг 2: подготовить набор данных
Теперь иметь необработанный текст и передавать его в классификатор не будет полезным или точным, поскольку каждый твит существенно отличается. К счастью, есть способы борьбы с текстом при попытке применить алгоритмы классификации или машинного обучения. Для этого примера мы собираемся использовать следующие два класса:
- Token Count Vectorizer : преобразует коллекцию образцов текста в вектор количества токенов. По сути, каждое слово в нашем твите становится уникальным числом и отслеживает количество вхождений слова в конкретном текстовом образце.
- Tf-idf Transformer : сокращение от частоты — обратная частота документа, это числовая статистика, предназначенная для отражения того, насколько важно слово для документа в коллекции или корпусе.
Давайте начнем с нашего текстового векторизатора:
<?php
...
use Phpml\FeatureExtraction\TokenCountVectorizer;
use Phpml\Tokenization\WordTokenizer;
...
$vectorizer = new TokenCountVectorizer(new WordTokenizer());
$vectorizer->fit($samples);
$vectorizer->transform($samples);
Затем примените Tf-idf Transformer:
<?php
...
use Phpml\FeatureExtraction\TfIdfTransformer;
...
$tfIdfTransformer = new TfIdfTransformer();
$tfIdfTransformer->fit($samples);
$tfIdfTransformer->transform($samples);
Наш массив сэмплов теперь находится в формате, где его легко понять с помощью нашего классификатора. Мы еще не закончили, нам нужно пометить каждый образец соответствующим настроением.
Шаг 3: Генерация учебного набора данных
К счастью, в PHP-ML эта потребность уже покрыта, и код довольно прост:
<?php
...
use Phpml\Dataset\ArrayDataset;
...
$dataset = new ArrayDataset($samples, $dataset->getTargets());
Мы могли бы пойти дальше и использовать этот набор данных и обучить наш классификатор. Однако нам не хватает тестового набора данных для использования в качестве проверки, поэтому мы собираемся немного «обмануть» и разделить наш исходный набор данных на два: обучающий набор данных и гораздо меньший набор данных, который будет использоваться для проверки точности нашего модель.
<?php
...
use Phpml\CrossValidation\StratifiedRandomSplit;
...
$randomSplit = new StratifiedRandomSplit($dataset, 0.1);
$trainingSamples = $randomSplit->getTrainSamples();
$trainingLabels = $randomSplit->getTrainLabels();
$testSamples = $randomSplit->getTestSamples();
$testLabels = $randomSplit->getTestLabels();
Этот подход называется перекрестной проверкой. Термин происходит из статистики и может быть определен следующим образом:
Перекрестная проверка, иногда называемая ротационной оценкой, представляет собой метод проверки модели для оценки того, как результаты статистического анализа будут обобщаться в независимый набор данных. Он в основном используется в условиях, когда целью является прогнозирование, и каждый хочет оценить, насколько точно прогностическая модель будет работать на практике. — Wikipedia.com
Шаг 4: Тренируйте классификатор
Наконец, мы готовы вернуться и реализовать наш класс SentimentAnalysis
Если вы еще не заметили, огромная часть машинного обучения связана со сбором и манипулированием данными; Реальная реализация моделей машинного обучения, как правило, менее сложна.
Для реализации нашего класса анализа настроений у нас есть три алгоритма классификации:
- Классификация опорных векторов
- KNearestNeighbors
- NaiveBayes
Для этого упражнения мы будем использовать самый простой из них, классификатор NaiveBayes, поэтому давайте продолжим и обновим наш класс для реализации метода train:
<?php
namespace PhpmlExercise\Classification;
use Phpml\Classification\NaiveBayes;
class SentimentAnalysis
{
protected $classifier;
public function __construct()
{
$this->classifier = new NaiveBayes();
}
public function train($samples, $labels)
{
$this->classifier->train($samples, $labels);
}
}
Как видите, мы позволяем PHP-ML сделать всю тяжелую работу за нас. Мы просто создаем красивую небольшую абстракцию для нашего проекта. Но как мы узнаем, что наш классификатор действительно тренируется и работает? Время использовать наши testSamples
testLabels
Шаг 5: Проверка точности классификатора
Прежде чем мы сможем приступить к тестированию нашего классификатора, мы должны реализовать метод прогнозирования:
<?php
...
class SentimentAnalysis
{
...
public function predict($samples)
{
return $this->classifier->predict($samples);
}
}
И снова, PHP-ML делает нас солидными и делает всю тяжелую работу за нас. Давайте обновим наш класс classifyTweets
<?php
...
$predictedLabels = $classifier->predict($testSamples);
Наконец, нам нужен способ проверить точность нашей обученной модели; К счастью, в PHP-ML это тоже есть, и у них есть несколько классов метрик. В нашем случае нас интересует точность модели. Давайте посмотрим на код:
<?php
...
use Phpml\Metric\Accuracy;
...
echo 'Accuracy: '.Accuracy::score($testLabels, $predictedLabels);
Мы должны увидеть что-то вроде:
Accuracy: 0.73651877133106%
Вывод
Эта статья немного упала, поэтому давайте подведем итоги того, что мы узнали до сих пор:
- Наличие хорошего набора данных с самого начала имеет решающее значение для реализации алгоритмов машинного обучения.
- Разница между контролируемым обучением и неконтролируемым обучением.
- Значение и использование перекрестной проверки в машинном обучении.
- Эта векторизация и преобразование необходимы для подготовки наборов текстовых данных для машинного обучения.
- Как реализовать анализ настроений в Twitter с помощью классификатора PHP-ML NaiveBayes.
Этот пост также послужил введением в библиотеку PHP-ML и, надеюсь, дал вам хорошее представление о том, что библиотека может делать и как ее можно встроить в ваши собственные проекты.
Наконец, этот пост ни в коем случае не является всеобъемлющим, и в нем есть чему поучиться, улучшить и поэкспериментировать; Вот несколько идей, которые помогут вам начать улучшать ситуацию:
- Замените алгоритм NaiveBayes алгоритмом классификации опорных векторов.
- Если вы попытаетесь запустить полный набор данных (14 000 строк), вы, вероятно, заметите, насколько интенсивным может быть процесс. Попробуйте реализовать постоянство модели, чтобы ее не нужно было тренировать при каждом запуске.
- Переместите генерацию набора данных в свой собственный вспомогательный класс.
Я надеюсь, что вы нашли эту статью полезной. Если у вас есть идеи по применению PHP-ML или какие-либо вопросы, не стесняйтесь оставлять их ниже в области комментариев!