Байесовская классификация — различия между версиями
Vlad (обсуждение | вклад) |
м (rollbackEdits.php mass rollback) |
||
(не показано 10 промежуточных версий 6 участников) | |||
Строка 27: | Строка 27: | ||
|definition = | |definition = | ||
'''Функционал среднего риска''' {{---}} ожидаемая величина потери при классификации объектов алгоритмом $a$: | '''Функционал среднего риска''' {{---}} ожидаемая величина потери при классификации объектов алгоритмом $a$: | ||
− | <tex> | + | :<tex> R(a) = \displaystyle\sum_{y \in Y}\sum_{s \in Y}\lambda_{ys}P_yP(A_s|y) </tex> |
− | R(a) = \displaystyle\sum_{y \in Y}\sum_{s \in Y}\lambda_{ys}P_yP(A_s|y) | ||
− | </tex> | ||
}} | }} | ||
Строка 38: | Строка 36: | ||
Если известны априорные вероятности $P_y$ и функции правдоподобия $p_y(x)$, | Если известны априорные вероятности $P_y$ и функции правдоподобия $p_y(x)$, | ||
то минимум среднего риска $R(a)$ достигается алгоритмом | то минимум среднего риска $R(a)$ достигается алгоритмом | ||
− | <tex> | + | :<tex> a(x) = \displaystyle\arg\min_{s \in Y}\sum_{y \in Y}\lambda_{ys}P_yp_y(x) </tex> |
− | a(x) = \displaystyle\arg\min_{s \in Y}\sum_{y \in Y}\lambda_{ys}P_yp_y(x) | ||
− | </tex> | ||
|proof= | |proof= | ||
Для произвольного $t \in Y$ запишем функционал среднего риска: | Для произвольного $t \in Y$ запишем функционал среднего риска: | ||
− | <tex> | + | :<tex> R(a)=\displaystyle\sum_{y \in Y}\sum_{s \in Y}\lambda_{ys}P_yP(A_s|y) = \sum_{y \in Y}\lambda_{yt}P_yP(A_t|y) + \sum_{s \in Y\setminus\{t\} }\sum_{y \in Y}\lambda_{ys}P_yP(A_s|y).</tex> |
− | R(a)=\displaystyle\sum_{y \in Y}\sum_{s \in Y}\lambda_{ys}P_yP(A_s|y) = | ||
− | \sum_{y \in Y}\lambda_{yt}P_yP(A_t|y) + \sum_{s \in Y\setminus\{t\} }\sum_{y \in Y}\lambda_{ys}P_yP(A_s|y). | ||
− | </tex> | ||
Применив формулу полной вероятности, $P(A_t \mid y) = 1 −\displaystyle\sum_{ s \in Y \setminus \{t\} }P(A_s \mid y)$, получим: | Применив формулу полной вероятности, $P(A_t \mid y) = 1 −\displaystyle\sum_{ s \in Y \setminus \{t\} }P(A_s \mid y)$, получим: | ||
− | <tex> | + | :<tex> R(a) = \displaystyle\sum_{y \in Y}\lambda_{yt}P_y + \sum_{ s \in Y \setminus \{t\} } \sum_{y \in Y} (\lambda_{ys} - \lambda_{yt})P_yP(A_s|y) = </tex> |
− | R(a) = \displaystyle\sum_{y \in Y}\lambda_{yt}P_y + \sum_{ s \in Y \setminus \{t\} } \sum_{y \in Y} | ||
− | (\lambda_{ys} - \lambda_{yt})P_yP(A_s|y) = | ||
− | </tex> | ||
− | <tex> | + | :<tex> = const(a) + \displaystyle\sum_{ s \in Y \setminus \{t\} } \int_{A_s}\sum_{y \in Y} (\lambda_{ys}−\lambda_{yt})P_yp_y(x)dx. </tex> |
− | = const(a) + \displaystyle\sum_{ s \in Y \setminus \{t\} } | ||
− | \int_{A_s}\sum_{y \in Y} (\lambda_{ys}−\lambda_{yt})P_yp_y(x)dx. | ||
− | </tex> | ||
Введём для сокращения записи обозначение | Введём для сокращения записи обозначение | ||
Строка 66: | Строка 53: | ||
$R(a) = const(a) + \displaystyle\sum_{ s \in Y \setminus \{t\} }\int_{A_s}(g_s(x)−g_t(x))dx$. | $R(a) = const(a) + \displaystyle\sum_{ s \in Y \setminus \{t\} }\int_{A_s}(g_s(x)−g_t(x))dx$. | ||
− | Минимум | + | Минимум интеграла достигается, когда $A_s$ совпадает с областью неположительности подынтегрального выражения. |
− | <tex> | + | :<tex> A_s=\{x \in X \mid g_s(x) \leq g_t(x), \forall t \in Y, t \leq s\}. </tex> |
− | A_s=\{x \in X \mid g_s(x) \leq g_t(x), \forall t \in Y, t \leq s\}. | ||
− | </tex> | ||
С другой стороны, $A_s=\{x \in X \mid a(x) = s\}$. Значит, $a(x) = s$ тогда и только тогда, когда | С другой стороны, $A_s=\{x \in X \mid a(x) = s\}$. Значит, $a(x) = s$ тогда и только тогда, когда | ||
− | $s= \displaystyle\arg\min_{t \in Y}g_t(x)$. | + | :$s= \displaystyle\arg\min_{t \in Y}g_t(x)$. |
}} | }} | ||
Строка 84: | Строка 69: | ||
Следовательно, функции правдоподобия классов представимы в виде: | Следовательно, функции правдоподобия классов представимы в виде: | ||
− | <tex> | + | :<tex> p_y(x) = \displaystyle\prod^n_{i=1}p_{yi}(\xi_i) </tex> |
− | p_y(x) = \displaystyle\prod^n_{i=1}p_{yi}(\xi_i) | ||
− | </tex> | ||
где $p_{yj}(\xi_j)$ плотность распределения значений $j$-го признака для класса $y$. | где $p_{yj}(\xi_j)$ плотность распределения значений $j$-го признака для класса $y$. | ||
Строка 93: | Строка 76: | ||
Подставим эмпирические оценки одномерных плотностей в байесовский классификатор. Получим алгоритм: | Подставим эмпирические оценки одномерных плотностей в байесовский классификатор. Получим алгоритм: | ||
− | <tex> | + | :<tex> a(x) = \displaystyle\arg\max_{y \in Y}(\ln\lambda_yP'_y + \sum^n_{j=1}\ln p'_{yj}(\xi_j)). </tex> |
− | a(x) = \displaystyle\arg\max_{y \in Y}(\ln\lambda_yP'_y + \sum^n_{j=1}\ln p'_{yj}(\xi_j)). | ||
− | </tex> | ||
Основные его преимущества {{---}} простота реализации и низкие вычислительные затраты при обучении и классификации. | Основные его преимущества {{---}} простота реализации и низкие вычислительные затраты при обучении и классификации. | ||
Строка 103: | Строка 84: | ||
Основной его недостаток {{---}} низкое качество классификации в общем случае. | Основной его недостаток {{---}} низкое качество классификации в общем случае. | ||
− | == Пример кода scikit-learn == | + | == Применение == |
+ | |||
+ | Из-за своего низкого качества классификации наивный байесовскими классификатор в основном он используется либо как эталон при экспериментальном сравнении алгоритмов, | ||
+ | либо как элементарный строительный блок в алгоритмических композициях. | ||
+ | |||
+ | Рассмотрим частое применение байесовского классификатора к задаче классификации документов по их содержимому, | ||
+ | а именно к классификации электронных писем на два класса {{---}} спам ($S$) и не-спам ($\displaystyle \neg S$), | ||
+ | предполагая что вероятность слов в тексте не зависит друг от друга: | ||
+ | |||
+ | Программные спам-фильтры, построенные на принципах наивного байесовского классификатора, делают «наивное» предположение о том, что события, | ||
+ | соответствующие наличию того или иного слова в электронном письме или сообщении, являются независимыми по отношению друг к другу. | ||
+ | Это упрощение в общем случае является неверным для естественных языков: | ||
+ | |||
+ | :<tex> P(a\ very\ close\ game) = P(a) \times P(very) \times P(close) \times P(game) </tex> | ||
+ | |||
+ | Исходя из такого предположения, для решения задачи классификации сообщений лишь на 2 класса: | ||
+ | $S$ (спам) и $H = \neg S$ («хэм», то есть не спам) из теоремы Байеса можно вывести следующую формулу оценки вероятности «спамовости» всего сообщения $D$, | ||
+ | содержащего слова $W_1, W_2, ... W_N$: | ||
+ | |||
+ | :<tex>\displaystyle p(S\mid D) = p(S\mid W_1, W_2, ... W_N) = \frac{p(W_1, W_2, ... W_N\mid S) \cdot p(S)}{p(W_1, W_2, ... W_N)} = </tex> [так как $W_i$ предполагаются независимыми] <tex>=</tex> | ||
+ | |||
+ | :<tex>= \displaystyle\frac{\prod_{i} p(W_i\mid S) \cdot p(S)}{p(W_1, W_2, ... W_N)} = \frac{\prod_{i}p(S\mid W_i)}{\prod_i(p(S\mid W_i)) + \left(\frac{p(\neg S)}{p(S)}\right)^{1-N} \cdot \prod_i p(\neg S\mid W_i)} </tex> | ||
+ | |||
+ | Результат $p$ обычно сравнивают с некоторым порогом (например, $0.5$), чтобы решить, является ли сообщение спамом или нет. Если $p$ ниже, чем порог, сообщение рассматривают как вероятный «ham», иначе его рассматривают как вероятный спам. | ||
+ | |||
+ | :<tex>\displaystyle\ln{p(S\mid D)\over p(\neg S\mid D)} > h</tex>. | ||
+ | |||
+ | ==Примеры кода== | ||
+ | ===Пример кода scikit-learn=== | ||
Классификатор [https://scikit-learn.org/stable/modules/generated/sklearn.naive_bayes.GaussianNB.html#sklearn.naive_bayes.GaussianNB GaussianNB] реализует наивный байесовский классификатор в предположении что изначальное распределение было гауссовым: | Классификатор [https://scikit-learn.org/stable/modules/generated/sklearn.naive_bayes.GaussianNB.html#sklearn.naive_bayes.GaussianNB GaussianNB] реализует наивный байесовский классификатор в предположении что изначальное распределение было гауссовым: | ||
− | <tex> | + | :<tex> P(x_i \mid y) = \displaystyle\frac{1}{\sqrt{2\pi\sigma^2_y}}\exp(-\frac{(x_i - \mu_y)^2}{2\sigma^2_y}) </tex> |
− | P(x_i \mid y) = \frac{1}{\sqrt{2\pi\sigma^2_y}}\exp(-\frac{(x_i - \mu_y)^2}{2\sigma^2_y}) | ||
− | </tex> | ||
'''from''' sklearn '''import''' datasets | '''from''' sklearn '''import''' datasets | ||
+ | '''from''' sklearn.metrics '''import''' f1_score, accuracy_score | ||
+ | '''from''' sklearn.naive_bayes '''import''' GaussianNB | ||
iris = datasets.load_iris() | iris = datasets.load_iris() | ||
− | |||
gnb = GaussianNB() | gnb = GaussianNB() | ||
− | + | pred = gnb.fit(iris.data, iris.target).predict(iris.data) | |
− | '''print'''(''" | + | accuracy = accuracy_score(iris.target, pred) |
− | + | f1 = f1_score(iris.target, pred, average="micro") | |
+ | '''print'''(''"accruracy:"'', accuracy, ''"f1:"'', f1) | ||
Вывод: | Вывод: | ||
− | + | accruracy: 0.96 f1: 0.96 | |
+ | |||
+ | ===Пример на языке Java=== | ||
+ | Пример классификации с применением <code>weka.classifiers.bayes.NaiveBayes</code><ref>[http://weka.sourceforge.net/doc.dev/weka/classifiers/bayes/NaiveBayes.html/ Weka, Naive Bayes]</ref> | ||
+ | |||
+ | <code>Maven</code> зависимость: | ||
+ | <dependency> | ||
+ | <groupId>nz.ac.waikato.cms.weka</groupId> | ||
+ | <artifactId>weka-stable</artifactId> | ||
+ | <version>3.8.0</version> | ||
+ | </dependency> | ||
+ | |||
+ | '''import''' weka.classifiers.bayes.NaiveBayes; | ||
+ | '''import''' weka.classifiers.evaluation.Evaluation; | ||
+ | '''import''' weka.core.converters.ConverterUtils; | ||
+ | '''import''' java.util.Random; | ||
+ | |||
+ | <font color="green">// load dataset</font> | ||
+ | '''var''' source = new DataSource("/iris.arff"); | ||
+ | '''var''' dataset = source.getDataSet(); | ||
+ | <font color="green">// set class index to the last attribute</font> | ||
+ | dataset.setClassIndex(dataset.numAttributes() - 1); | ||
+ | <font color="green">// create and build the classifier</font> | ||
+ | '''var''' nb = new NaiveBayes(); | ||
+ | nb.buildClassifier(dataset); | ||
+ | <font color="green">// cross validate model</font> | ||
+ | var eval = new Evaluation(dataset); | ||
+ | eval.crossValidateModel(nb, dataset, 10, new Random(41)); | ||
+ | System.out.println("Estimated Accuracy: "+ Double.toString(eval.pctCorrect())); | ||
+ | |||
+ | === Пример на языке R === | ||
+ | {{Main|Примеры кода на R}} | ||
+ | |||
+ | <font color="gray"># importing package and it's dependencies</font> | ||
+ | library(e1071) | ||
+ | |||
+ | <font color="gray"># reading data</font> | ||
+ | data <- read.csv(<font color="green">"input.csv"</font>, <font color="#660099">sep</font> = <font color="green">','</font>, <font color="#660099">header</font> = FALSE) | ||
+ | |||
+ | <font color="gray"># splitting data into training and test data sets</font> | ||
+ | index <- createDataPartition(<font color="#660099">y</font> = data$<strong><font color="#660E7A">target</font></strong>, <font color="#660099">p</font> = <font color="blue">0.8</font>, <font color="#660099">list</font> = FALSE) | ||
+ | training <- data[index,] | ||
+ | testing <- data[-index,] | ||
+ | |||
+ | <font color="gray"># create objects x and y for predictor and response variables</font> | ||
+ | x <- training[, -<font color="blue">9</font>] | ||
+ | y <- training$<strong><font color="#660E7A">target</font></strong> | ||
+ | |||
+ | <font color="gray"># training model</font> | ||
+ | model <- train(x, y, <font color="green">'nb'</font>, <font color="#660099">trControl</font> = trainControl(<font color="#660099">method</font> = <font color="green">'cv'</font>, <font color="#660099">number</font> = <font color="blue">10</font>)) | ||
+ | |||
+ | <font color="gray"># predicting results</font> | ||
+ | predictions <- predict(model, <font color="#660099">newdata</font> = testing) | ||
==См. также== | ==См. также== |
Текущая версия на 19:06, 4 сентября 2022
Содержание
Вероятностная постановка задачи классификации
Пусть $X$ множество объектов, $Y$ конечное множество имён классов, множество $X \times Y$ является вероятностным пространством с плотностью распределения $p(x,y)=P(y)p(x|y)$. Вероятности появления объектов каждого из классов $P_y=P(y)$ называются априорными вероятностями классов. Плотности распределения $p_y(x)=p(x|y)$ называются функциями правдоподобия классов.
Вероятностная постановка задачи классификации разделяется на две независимые подзадачи:
- Имеется простая выборка $X^l=(x_i, y_i)^l_{i=1}$ из неизвестного распределения $p(x,y)=P_yp_y(x)$. Требуется построить эмпирические оценки априорных вероятностей $P'_y$ и функций правдоподобия $p'_y(x)$ для каждого из классов $y \in Y$.
- По известным плотностям распределения $p_y(x)$ и априорным вероятностям $P_y$ всех классов $y \in Y$ построить алгоритм $a(x)$, минимизирующий вероятность ошибочной классификации.
Априорные вероятности классов $P_y$ можно оценить согласно закону больших чисел, тогда частота появления объектов каждого из классов равна $P'_y=\frac{l_y}{l}$ где $l_y=|X^l_y|, y \in Y$ сходится по вероятности к $P_y$ при $l_y \to \infty$. Чем больше длина выборки, тем точнее выборочная оценка $P'_y$.
Оптимальный байесовский классификатор
Рассмотрим произвольный алгоритм $a:X \to Y$. Он разбивает множество $X$ на не пересекающиеся области $A_y=\{x \in X | a(x) = y\}, y \in Y$. Вероятность того,что появится объект класса $y$ и алгоритм $a$ отнесёт его к классу $s$, равна $P_yP(A_s|y)$. Каждой паре $(y,s) \in Y \times Y$ поставим в соответствие величину потери $\lambda_{ys}$ при отнесении объекта класса $y$ к классу $s$.
Определение: |
Функционал среднего риска — ожидаемая величина потери при классификации объектов алгоритмом $a$:
|
Теорема (об оптимальности байесовского классификатора): |
Если известны априорные вероятности $P_y$ и функции правдоподобия $p_y(x)$,
то минимум среднего риска $R(a)$ достигается алгоритмом |
Доказательство: |
Для произвольного $t \in Y$ запишем функционал среднего риска: Применив формулу полной вероятности, $P(A_t \mid y) = 1 −\displaystyle\sum_{ s \in Y \setminus \{t\} }P(A_s \mid y)$, получим: Введём для сокращения записи обозначение $g_s(x) = \displaystyle\sum_{y \in Y}\lambda_{ys}P_yp_y(x)$, тогда $R(a) = const(a) + \displaystyle\sum_{ s \in Y \setminus \{t\} }\int_{A_s}(g_s(x)−g_t(x))dx$. Минимум интеграла достигается, когда $A_s$ совпадает с областью неположительности подынтегрального выражения. С другой стороны, $A_s=\{x \in X \mid a(x) = s\}$. Значит, $a(x) = s$ тогда и только тогда, когда
|
Наивный байесовский классификатор
Допустим, что объекты $x \in X$ описываются $n$ числовыми признаками $f_j:X→R,j= 1,...,n$. Обозначим через $x = (\xi_1,...,\xi_n)$ произвольный элемент пространства объектов $X=R^n$, где $\xi_j=f_j(x)$.
Предположим, что признаки $f_1(x),...,f_n(x)$ являются независимыми случайными величинами. Следовательно, функции правдоподобия классов представимы в виде:
где $p_{yj}(\xi_j)$ плотность распределения значений $j$-го признака для класса $y$. Алгоритмы классификации исходящие из этого предположения, называются наивными байесовскими.
Подставим эмпирические оценки одномерных плотностей в байесовский классификатор. Получим алгоритм:
Основные его преимущества — простота реализации и низкие вычислительные затраты при обучении и классификации. В тех редких случаях, когда признаки почти независимы, наивный байесовский классификатор близок к оптимальному. Достаточно малое количество данных необходимо для обучения, оценки параметров и классификации.
Основной его недостаток — низкое качество классификации в общем случае.
Применение
Из-за своего низкого качества классификации наивный байесовскими классификатор в основном он используется либо как эталон при экспериментальном сравнении алгоритмов, либо как элементарный строительный блок в алгоритмических композициях.
Рассмотрим частое применение байесовского классификатора к задаче классификации документов по их содержимому, а именно к классификации электронных писем на два класса — спам ($S$) и не-спам ($\displaystyle \neg S$), предполагая что вероятность слов в тексте не зависит друг от друга:
Программные спам-фильтры, построенные на принципах наивного байесовского классификатора, делают «наивное» предположение о том, что события, соответствующие наличию того или иного слова в электронном письме или сообщении, являются независимыми по отношению друг к другу. Это упрощение в общем случае является неверным для естественных языков:
Исходя из такого предположения, для решения задачи классификации сообщений лишь на 2 класса: $S$ (спам) и $H = \neg S$ («хэм», то есть не спам) из теоремы Байеса можно вывести следующую формулу оценки вероятности «спамовости» всего сообщения $D$, содержащего слова $W_1, W_2, ... W_N$:
- [так как $W_i$ предполагаются независимыми]
Результат $p$ обычно сравнивают с некоторым порогом (например, $0.5$), чтобы решить, является ли сообщение спамом или нет. Если $p$ ниже, чем порог, сообщение рассматривают как вероятный «ham», иначе его рассматривают как вероятный спам.
- .
Примеры кода
Пример кода scikit-learn
Классификатор GaussianNB реализует наивный байесовский классификатор в предположении что изначальное распределение было гауссовым:
from sklearn import datasets from sklearn.metrics import f1_score, accuracy_score from sklearn.naive_bayes import GaussianNB iris = datasets.load_iris() gnb = GaussianNB() pred = gnb.fit(iris.data, iris.target).predict(iris.data) accuracy = accuracy_score(iris.target, pred) f1 = f1_score(iris.target, pred, average="micro") print("accruracy:", accuracy, "f1:", f1)
Вывод:
accruracy: 0.96 f1: 0.96
Пример на языке Java
Пример классификации с применением weka.classifiers.bayes.NaiveBayes
[1]
Maven
зависимость:
<dependency> <groupId>nz.ac.waikato.cms.weka</groupId> <artifactId>weka-stable</artifactId> <version>3.8.0</version> </dependency>
import weka.classifiers.bayes.NaiveBayes; import weka.classifiers.evaluation.Evaluation; import weka.core.converters.ConverterUtils; import java.util.Random;
// load dataset var source = new DataSource("/iris.arff"); var dataset = source.getDataSet(); // set class index to the last attribute dataset.setClassIndex(dataset.numAttributes() - 1); // create and build the classifier var nb = new NaiveBayes(); nb.buildClassifier(dataset); // cross validate model var eval = new Evaluation(dataset); eval.crossValidateModel(nb, dataset, 10, new Random(41)); System.out.println("Estimated Accuracy: "+ Double.toString(eval.pctCorrect()));
Пример на языке R
# importing package and it's dependencies library(e1071) # reading data data <- read.csv("input.csv", sep = ',', header = FALSE) # splitting data into training and test data sets index <- createDataPartition(y = data$target, p = 0.8, list = FALSE) training <- data[index,] testing <- data[-index,] # create objects x and y for predictor and response variables x <- training[, -9] y <- training$target # training model model <- train(x, y, 'nb', trControl = trainControl(method = 'cv', number = 10)) # predicting results predictions <- predict(model, newdata = testing)