Модель обнаружения поддельной работы

Есть много вариантов использования моделей машинного обучения. Текстовая аналитика, ветвь обработки естественного языка, предоставляет способы, позволяющие применять алгоритмы машинного обучения к текстовым данным для моделей классификации. Паттерны существуют в данных способами, которые мы обычно не можем обнаружить постоянно, но с помощью аналитических инструментов мы можем обнаружить эти существующие закономерности. Эти шаблоны даже стали проще и масштабируемыми с помощью машинного обучения, и типичным примером, с которым мы, вероятно, сталкиваемся каждый день, является классификатор спама.

В этой статье рассказывается, как можно использовать представление текстовых данных в кодировках, которые затем могут быть введены в вашу модель машинного обучения. Данные для этого можно скачать здесь;

Первая процедура - провести небольшой исследовательский анализ данных. Это сделано для того, чтобы познакомить нас с набором данных. Загружаем наш набор данных и вызываем нужные библиотеки;

library(tidytext)
library(tidyverse)
library(tidygraph)
library(tm)
library(SnowballC)
library(randomForest)

#Read in the dataset
posting <- read.csv("fake_job_postings.csv")

После загрузки данных мы визуализируем количество вакансий в каждой отрасли. Обратите внимание: t 0 в столбце мошенничества представляет реальные вакансии, а 1 - поддельные вакансии.



Визуализация основных отраслей для объявлений о вакансиях как на поддельных, так и на реальных вакансиях согласно столбцу мошенничества;

posting %>% 
  group_by(fraudulent) %>% 
  count(industry) %>% 
  arrange(desc(n)) %>%
  top_n(10) %>% 
  mutate_if(is.character, list(~na_if(.,""))) %>% 
  mutate(total = paste(round(100*n/sum(n), 1), "%", sep = "")) %>% 
  filter(fraudulent == 1)%>% 
  ggplot(., aes(reorder(industry, n), n, fill = industry)) +
  geom_bar(stat = "identity", show.legend = F) +
  coord_flip() + 
  geom_text(aes(label = total), color = "black", size = 4) +
  labs(title = "Fake Posts", subtitle = "Count by Industry",
       x = 'Industry') + theme_classic()

Визуализация топовых индустрий для реальных постов;

Мы видим, что в обоих случаях большая часть вакансий не указывает свою отрасль, и поэтому нельзя судить о том, является ли уведомление о вакансии настоящим или фальшивым.

Взглянув на требования к вакансиям для каждого типа вакансий, то есть настоящих или фальшивых, мы копаемся, чтобы найти некоторую информацию;

#Job Requirements
posting %>% 
  group_by(fraudulent) %>% 
  count(required_experience) %>% 
  mutate(percentage = paste(round(100*n/sum(n), 1), "%", sep = "")) %>% 
  mutate_if(is.character, list(~na_if(.,""))) %>% 
  ggplot(., aes(reorder(required_experience, n), log(n), fill = factor(fraudulent))) +
  geom_bar(stat = "identity") +
  geom_text(aes(label = percentage)) +
  labs(x = "Job Requirement", title = "Jobs Requirement") +
  scale_fill_discrete(name = "Dose", labels = c("Not Fake", "Fake")) +
  coord_flip() +
  theme_classic()

Зарегистрированное значение было взято, чтобы лучше визуализировать ось. Отсюда мы также видим, что большая часть реальных рабочих мест не определяет своих образовательных требований.

Изучение категорий вакансий, на которых были логотипы компаний. Интересно, что вы обнаружите, что у большинства компаний с фактическими объявлениями о вакансиях нет логотипов, в то время как для поддельных сообщений все наоборот. Возможно, чтобы убедиться, что они выглядят настоящими? Я имею в виду, что любой теневой бизнес хотел бы хорошо замести следы, следовательно, возникла необходимость в логотипе.

С этого момента мы переходим к самой процедуре предварительной обработки, чтобы мы могли передать данные в алгоритм машинного обучения.

Предварительная обработка / очистка данных

Сначала мы удалим стоп-слова, знаки препинания, гиперссылки и другие нежелательные слова.

#Data Cleaning
corpus <- Corpus(VectorSource(posting$description))
inspect(corpus[1:5])
#Convert to lower cast
corpus <- tm_map(corpus, tolower)
#Remove Punctuationa and inspect
corpus <- tm_map(corpus, removePunctuation)
inspect(corpus[1:5])
#Remove stopwords
corpus <- tm_map(corpus, removeWords, stopwords(kind = "en"))
#Stem document
corpus <- tm_map(corpus, stemDocument)

Мы создадим термодокументную матрицу очищенных данных, что является основным этапом предварительной обработки.

#Create document frequency
freq <- DocumentTermMatrix(corpus)
freq
#remove sparsity
freq_sparsed <- removeSparseTerms(freq, sparse = 0.995)
freq_sparsed
#Convert cleaned document to a df
df <- as.data.frame(as.matrix(freq_sparsed))
#Give unique names to colnames
colnames(df) <- make.names(colnames(df))
#Add the fraudelent colum
df$fradulent <- posting$fraudulent
#Remove duplicate column names
colnames(df) <-  make.unique(colnames(df), sep = "_")

На изображении выше должен выглядеть ваш новый фрейм данных, когда вы выполняете операцию Term-Document Frequency.

Разделение для поезда и тестового набора

Мы будем использовать компонент split в библиотеке caTools и каретку для нашей модели машинного обучения;

#Create train and test dataset
library(caTools)
library(caret)
#Convert target variable to factor type
df$fradulent <- as.factor(df$fradulent)
#Split
set.seed(2020, sample.kind = "Rounding")
test_index <- createDataPartition(y = df$fradulent, times = 1, p = 0.1, list= FALSE)
train_set <- df[-test_index, ]
validation <- df[test_index, ]
#Check the split ratio of the target variable
table(train_set$fradulent)
table(validation$fradulent)

Из-за вычислительного времени, а также наличия огромного дисбаланса классов мы просто будем использовать 1000 случайных выборок данных, однако вы можете решить обучаться на всем наборе данных, если вам это подходит.

library(caret)
#Get a sample number
n <- 1000
#Cross validation contro;
control<- trainControl(method = "cv", number = 5, verboseIter = TRUE)
#Number of trees for each train
grid <-data.frame(mtry = c(1, 5, 10, 25, 50, 100))
#create a sample from n number from dataset
index <- sample(nrow(train_set), n)
#subset data
rf_train_data <- train_set[index, ]
#Train random forest model
subset_train_rf <- train(fradulent ~ ., method = "rf", data = rf_train_data, ntree = 150, trControl = control, tuneGrid = grid)
#Accuracy on the sample train data
prediction <- predict(subset_train_rf, rf_train_data)
#Confusion matrix
confusionMatrix(prediction, rf_train_data$fradulent)

97,6% в данных поезда выглядит хорошо, но нам нужно увидеть, насколько хорошо он работает на более раннем разбиении набора проверки (хотя мы использовали небольшой набор из 1000 строк обучить данные).

#predict on the train set
prediction01 <- predict(subset_train_rf, validation)
#confusion matrix
confusionMatrix(validation$fradulent, prediction01)

Интересно, что модель по-прежнему хорошо работает с набором для проверки, состоящим из выборки более 1800.

Вы также можете использовать другие модели, но в моей модели я использовал перекрестную проверку в K-кратном порядке, чтобы справиться с переобучением, которое в значительной степени возможно из-за дисбаланса классов. Наконец, поиск по сетке был использован для выбора оптимальных гиперпараметров для модели.

Заключение

Обратите внимание, что только столбец описания для функций рядом с целевым столбцом, который является мошенническим столбцом, является единственными используемыми столбцами. Однако вы можете решить использовать все остальные столбцы либо путем их кодирования. Например, такие столбцы, как has_company_logo и удаленная работа, можно просто добавить к фрейму данных после создания Term-Document и, таким образом, включить. Другие столбцы, например, промышленность, можно просто закодировать и добавить.

Однако следует учитывать, что вы создаете больше функций и, следовательно, увеличиваете время вычислений.

Получите доступ к экспертному обзору - Подпишитесь на DDI Intel