Анализ настроений, часть 3 (интерфейс)

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

Вы можете скачать исходный код отсюда.

Итак, давайте начнем создавать наш проект «Интерфейс».

Давайте добавим новый проект в наше решение, выберите «Приложение Windows Forms», как показано ниже, и нажмите «Далее».

Затем назовите его «MoviewReiews» и нажмите «Создать», чтобы создать и добавить новый проект в наше решение.

Теперь у нас есть проект интерфейса, который мы назвали «MovieReviews», теперь мы можем начать над ним работать.

Прежде чем начать, позвольте мне объяснить, что будет делать этот проект «Интерфейс». Здесь мы собираемся использовать нашу обученную модель, чтобы прогнозировать отзывы пользователей о любом фильме.

Как вы уже догадались, нам нужны новые «Отзывы пользователей», чтобы иметь возможность предсказать, является ли он «положительным» или «отрицательным». Мы собираемся упростить для пользователей ввод названия любого фильма и получить общую информацию о нем с помощью отзывы пользователей. Мы собираемся использовать Html Agility Pack, чтобы получить общую информацию о фильме и отзывы пользователей из IMDB.

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

И второй инструмент 3-й части, который мы собираемся использовать, это Metro Set UI, это наш ключевой инструмент для разработки лучшего интерфейса.

Пожалуйста, установите Html Agility Pack и Metro Set UI из диспетчера пакетов Nuget.

Убедитесь, что вы выбрали вкладку Обзор, а затем найдите пакеты Html Agility Pack и Metro Set UI, как показано ниже, и установите их в только что созданный проект.

Пожалуйста, установите Html Agility Pack, как показано ниже.

Пожалуйста, установите Metro Set UI, как показано ниже.

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

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

Итак, давайте начнем с Settings.cs, это очень простой статический класс, в котором мы собираемся сохранить некоторые постоянные настройки, ниже вы можете указать свой ключ API, который вы получили из OMDB API, замените его разделом [APIKEY].

public static class Settings
    {
        public static string BASE_MOVIE_URL = @"https://www.imdb.com/title/{0}";
        public static string BASE_MOVIE_URL_REVIEWS =  @"https://www.imdb.com/title/{0}/reviews?ref_=tt_ov_rt";
        public static string API_SEARCH_URL =  @"https://www.omdbapi.com/?t={0}&apikey=[APIKEY]";
    }

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

У класса Web Helper есть несколько вспомогательных функций, давайте начнем с их определения. Ниже вы можете увидеть пять вспомогательных функций, которые помогут нам получить информацию и отзывы пользователей о фильмах, как вы можете догадаться, здесь мы используем Html Agility Pack, я не буду говорить об этом, потому что эта статья не охватывает это, и еще раз хочу уточнить, что это образовательная статья и обучающий проект по нему, потому я никому не рекомендую делать парсинг на каком-либо сайте.

#region HtmlHelpers
        
        //Gets reviews nodes
        private static IEnumerable<HtmlNode> GetReviewsNodes(HtmlDocument doc)
        {
            return doc.DocumentNode.SelectNodes("//div[contains(@class,  'lister-item-content')]");
        }

        //Gets rating value
        private static string GetRating(HtmlNode node)
        {
            var mainNode = node.SelectSingleNode(".//span[contains(@class,  'rating-other-user-rating')]");
            return mainNode != null ? mainNode.ChildNodes[3].InnerHtml : "N/A";
        }

        //Gets title
        private static string GetTitle(HtmlNode node)
        {
            return node.SelectSingleNode(".//a[contains(@class,  'title')]").InnerHtml;
        }

        //Gets user and date
        private static string[] GetUserNameAndData(HtmlNode node)
        {
            var mainNode = node.SelectSingleNode(".//div[contains(@class,  'display-name-date')]");
            var userNode = mainNode.SelectSingleNode(".//span[contains(@class,  'display-name-link')]");
            var user = userNode.SelectSingleNode("a").InnerHtml;
            var date = mainNode.SelectSingleNode(".//span[contains(@class,  'review-date')]").InnerHtml;
            return new[] {user, date};
        }

        //Gets title
        private static string GetReview(HtmlNode node)
        {
            return node.SelectSingleNode(".//div[contains(@class, 'text  show-more__control')]").InnerHtml;
        }

   #endregion

Нет, нам нужно создать «Пользовательский элемент управления» и назвать его «ReviewItem», мы собираемся использовать этот элемент управления для отображения фильма. Отзывы. Как вы можете видеть ниже, есть несколько полей, которые мы собираемся использовать для отображения информации об обзоре.

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

давайте начнем с класса MovieSearchResultModel, это класс, который нам нужно использовать для чтения JSON-ответа OMDB API, мы не собираемся использовать все поля, но как вы можете см. структуру, как показано ниже.

public class Rating
    {
        public string Source { get; set; }
        public string Value { get; set; }
    }
    public class MovieSearchResultModel
    {
        public string Title { get; set; }
        public string Year { get; set; }
        public string Rated { get; set; }
        public string Released { get; set; }
        public string Runtime { get; set; }
        public string Genre { get; set; }
        public string Director { get; set; }
        public string Writer { get; set; }
        public string Actors { get; set; }
        public string Plot { get; set; }
        public string Language { get; set; }
        public string Country { get; set; }
        public string Awards { get; set; }
        public string Poster { get; set; }
        public List<Rating> Ratings { get; set; }
        public string Metascore { get; set; }
        public string imdbRating { get; set; }
        public string imdbVotes { get; set; }
        public string imdbID { get; set; }
        public string Type { get; set; }
        public string DVD { get; set; }
        public string BoxOffice { get; set; }
        public string Production { get; set; }
        public string Website { get; set; }
        public bool Response { get; set; }
        public string PageUrl { get; set; }
        public string ReviewsPageUrl { get; set; }
    }

и второй режим, ReviewModel, который является моделью представления отзывов.

public class ReviewsModel
    {
        public string Title { get; set; }
        public string Review { get; set; }
        public string Rating { get; set; }
        public string User { get; set; }
        public string Date { get; set; }
    }

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

Мы используем здесь Metro Set UI для компонентов пользовательского интерфейса. внедрить Metro Set UI в свой проект очень просто. Вы можете проверить исходный код или проверить его на веб-сайте Metro Set UI.

Здесь часть кода главного экрана, мы начинаем с определения нашей переменной Debug, которую мы уже используем в проекте Trainer. Обратите внимание, что если вы установили эту переменную в «true» в проекте «Trainer», вам нужно установить «true» и здесь, они должны быть одинаковыми, иначе проект «Trainer» сохраняет модель в другой путь, а проект Movie Reviews ищет модель по другому пути, где, скорее всего, нет никакой модели.

private static readonly bool _debugMode = true;

Затем мы определяем SentimentAnalyst, который мы собираемся использовать, и назначаем ему информацию о пути к модели в соответствии со значением «отладки».

private SentimentAnalyst _sentimentAnalyst;
       
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            var modelDataFile = Path.Combine(GetParentDirectory(),
                _debugMode
                    ? $@"Movie Reviews\\Movie Reviews\\bin\\{"Debug"}\\Data"
                    : $@"Movie Reviews\\Movie Reviews\\bin\\{"Release"}\\Data",
                "model.zip");
            SetColors();
            _sentimentAnalyst = new SentimentAnalyst(null, modelDataFile);
            _sentimentAnalyst.LoadTrainedModel();
        }

На главном экране у нас есть несколько вспомогательных функций, давайте их тоже определим, эти функции помогут нам загружать отзывы пользователей, получать родительский каталог и устанавливать цвета некоторых элементов пользовательского интерфейса.

#region Helpers
       
         //Load user reviews into the list
        private void LoadReviews(IEnumerable<ReviewsModel> reviews)
        {
            var top = 0;
            reviewList.Controls.Clear();
            if(reviews==null) return;
            
            foreach (var review in reviews)
            {
                var data = new Data {Review = review.Review};
                var status = _sentimentAnalyst.Predicate(data);
                var reviewItem = new ReviewItem
                {
                    Width = 485,
                    Top = top,
                    lblTitle = {Text = review.Title},
                    lblDate = {Text = review.Date},
                    lblReview = {Text = review.Review},
                    lblUser = {Text = review.User},
                    lblRank = {Text = $@"{review.Rating}/10"},
                    lblStatus = { Text =   status.PredictionValue==true?"Positive":"Negative" }
                };
                reviewItem.lblStatus.BackColor = status.PredictionValue ?  Color.Green : Color.DarkRed;
                reviewList.Controls.Add(reviewItem);
                top += 180;
            }
        }

        //Gets solution path
        private static string GetParentDirectory()
        {
            var directoryInfo =  Directory.GetParent(Directory.GetCurrentDirectory()).Parent;
            if (directoryInfo?.Parent?.Parent != null)
                return directoryInfo.Parent.Parent
                    .FullName;
            return string.Empty;
        }

        //Set colors for the UI elements
        private void SetColors()
        {
            lblInfo1.ForeColor=Color.FromArgb(170,170,170);
            lblInfo2.ForeColor = Color.FromArgb(170, 170, 170);
            lblInfo3.ForeColor = Color.FromArgb(170, 170, 170);
            lblInfo4.ForeColor = Color.FromArgb(170, 170, 170);
            lblInfo5.ForeColor = Color.FromArgb(170, 170, 170);
            lblInfo6.ForeColor = Color.FromArgb(170, 170, 170);
            lblInfo7.ForeColor = Color.FromArgb(170, 170, 170);
            lblInfo8.ForeColor = Color.FromArgb(170, 170, 170);
            lblInfo9.ForeColor = Color.FromArgb(170, 170, 170);
            lblInfo10.ForeColor = Color.FromArgb(170, 170, 170);
            lblTitle.ForeColor = Color.FromArgb(170, 170, 170);
            lblYear.ForeColor = Color.FromArgb(170, 170, 170);
            lblReleased.ForeColor = Color.FromArgb(170, 170, 170);
            lblRated.ForeColor = Color.FromArgb(170, 170, 170);
            lblRuntime.ForeColor = Color.FromArgb(170, 170, 170);
            lblGenre.ForeColor = Color.FromArgb(170, 170, 170);
            lblLanguage.ForeColor = Color.FromArgb(170, 170, 170);
            lblCountry.ForeColor = Color.FromArgb(170, 170, 170);
            imdbRating.ForeColor = Color.FromArgb(170, 170, 170);
            lblInfo11.ForeColor = Color.FromArgb(170, 170, 170);
            lblPlot.ForeColor = Color.FromArgb(170, 170, 170);
        }

        #endregion Helpers

Итак, в заключение, мы собираемся написать код для события щелчка btnAnalyze, эта функция проверяет, вводит ли пользователь какое-либо имя фильма, если есть имя фильма, она вызывает вспомогательный класс, который мы определили выше, чтобы получить общую информацию, такую ​​​​как название, да, дата выпуска и т.д.

После этого он снова вызывает WebHelper, чтобы получить обзоры фильмов и передать их в качестве параметров другому вспомогательному классу, который называется LoadReviews для создания и загрузки обзоров пользователей в качестве пользовательских элементов управления на интерфейс.

private void btnAnalyze_Click(object sender, EventArgs e)
        {
            if (txtMoviewName.Text == string.Empty)
            {
                MessageBox.Show(this, "Please type a movie name", "Warning",  MessageBoxButtons.OK, MessageBoxIcon.Warning);
                txtMoviewName.Focus();
                return;
            }
            var movieInfo= WebHelper.GetMovieGeneralInfo(txtMoviewName.Text);
            if (movieInfo.Response)
            {
                lblTitle.Text = movieInfo.Title;
                lblYear.Text = movieInfo.Year;
                lblReleased.Text = movieInfo.Released;
                lblRated.Text = $@"{movieInfo.Rated}/10";
                lblRuntime.Text = movieInfo.Runtime;
                lblGenre.Text = movieInfo.Genre;
                lblPlot.Text = movieInfo.Plot;
                lblLanguage.Text = movieInfo.Language;
                lblCountry.Text = movieInfo.Country;
                imdbRating.Text = movieInfo.imdbRating;
                lblInfo11.Text = $@"{movieInfo.imdbRating}/10";
                if (movieInfo.Poster != null)
                    if(movieInfo.Poster!= "N/A")
                        moviePicture.Load(movieInfo.Poster);
                //Get movie reviews
                LoadReviews(WebHelper.GetMovieReviews(movieInfo.ReviewsPageUrl));
            }
            else
            {
                lblTitle.Text = string.Empty;
                lblYear.Text = string.Empty;
                lblReleased.Text = string.Empty;
                lblRated.Text = string.Empty;
                lblRuntime.Text = string.Empty;
                lblGenre.Text = string.Empty;
                lblPlot.Text = string.Empty;
                lblLanguage.Text = string.Empty;
                lblCountry.Text = string.Empty;
                imdbRating.Text = string.Empty;
                moviePicture.Image = null;
                reviewList.Controls.Clear();
                MessageBox.Show(this, "Cannot find the movie, please check the  name and try again.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                txtMoviewName.Focus();
            }
}

Подождите, где здесь ML.Net?

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

Здесь внутри вспомогательной функции LoadReviews мы вызвали прогноз для каждого обзора фильма.

var status = _sentimentAnalyst.Predicate(data);

Когда мы запускаем приложение и пишем название фильма, а затем нажимаем «Начать анализ», мы получаем результаты, подобные приведенным ниже, где каждый отзыв пользователя имеет «зеленый» или «красный» цвет с меткой «положительный» или «отрицательный».

Когда вы читаете отзывы пользователей, вы видите, что ML.Net работает действительно очень хорошо и дает очень точные прогнозы.

На сегодня это все о проекте «Анализ настроений», надеюсь, вы нашли для себя что-то полезное и чему-то научились. Далее мы поговорим и создадим пример приложения о мультиклассификации.