Благодаря растущей волне искусственного интеллекта пользователи в наши дни стали ожидать, что приложения будут умными и осведомленными об условиях, в которых они используются. IBM Watson предлагает множество связанных с естественным языком сервисов, которые вы можете использовать для создания таких приложений.
Например, вы можете использовать его службу Natural Language Understanding для извлечения ключевых слов, сущностей, настроений и множества других семантических деталей из любого текста, который читает пользователь. А если текст написан на иностранном языке, вы можете использовать службу « Переводчик языков», чтобы определить язык и перевести его на тот язык, который понимает пользователь.
В этом уроке я собираюсь познакомить вас с некоторыми из этих сервисов, показывая, как создать приложение, которое может переводить немецкие веб-страницы на английский и извлекать из них чувства, важные сущности и эмоции.
Прежде чем продолжить, я предлагаю вам прочитать следующее вводное руководство по службам IBM Watson:
1. Активация Сервисов
Сегодня мы будем работать с тремя службами Watson, и каждый из них необходимо активировать отдельно. Поэтому откройте свою панель инструментов IBM Bluemix и нажмите кнопку Создать .
Первый сервис, который мы собираемся активировать, это сервис преобразования документов , который позволяет нам конвертировать документы HTML, PDF и DOCX в обычный текст или JSON. Выберите его из каталога, дайте ему осмысленное имя и нажмите кнопку « Создать» .
Далее вернитесь в каталог и выберите услугу « Языковой переводчик» . Он поддерживает несколько широко распространенных языков и может по умолчанию обрабатывать текст в трех областях: новости, разговоры и патенты. Хотя первые два домена подходят для большинства текстов, последний домен может быть более точным для текстов, содержащих множество технических или юридических терминов.
На странице конфигурации дайте сервису осмысленное имя и нажмите кнопку « Создать» .
Вернитесь в каталог и выберите услугу « Понимание естественного языка» . Мы будем использовать этот сервис для извлечения чувств, сущностей и эмоций из неструктурированного текста. Опять же, дайте ему осмысленное имя на экране конфигурации и нажмите кнопку « Создать» .
Если вы откроете панель мониторинга прямо сейчас, вы сможете увидеть что-то вроде этого:
Все три службы имеют уникальные учетные данные, связанные с ними. Вы должны записать их все, потому что они понадобятся вам позже. Чтобы определить учетные данные какой-либо службы, выберите ее на панели инструментов, откройте вкладку « Учетные данные службы » и нажмите кнопку « Просмотреть учетные данные» .
2. Настройка проекта
Чтобы иметь возможность использовать эти три службы в проекте Android Studio, мы должны добавить Watson Java SDK в качестве зависимости implementation
в файл build.gradle модуля app
.
1
|
implementation ‘com.ibm.watson.developer_cloud:java-sdk:3.9.1’
|
Кроме того, мы будем использовать библиотеку Fuel в качестве HTTP-клиента, поэтому добавим ее также в качестве зависимости implementation
.
1
|
implementation ‘com.github.kittinunf.fuel:fuel-android:1.10.0’
|
Как Fuel, так и Watson Java SDK могут работать только в том случае, если наше приложение имеет разрешение INTERNET
, поэтому запросите его в файле манифеста.
1
|
<uses-permission android:name=»android.permission.INTERNET»/>
|
Затем добавьте теги <string>
содержащие имена пользователей и пароли всех трех служб, в файл strings.xml .
1
2
3
4
5
6
7
8
|
<string name=»document_conversion_username»>USERNAME1</string>
<string name=»document_conversion_password»>PASSWORD1</string>
<string name=»language_translator_username»>USERNAME2</string>
<string name=»language_translator_password»>PASSWORD2</string>
<string name=»natural_language_understanding_username»>USERNAME3</string>
<string name=»natural_language_understanding_password»>PASSWORD3</string>
|
И наконец, для краткости нашего кода в этом руководстве мы будем использовать Kotlin вместо Java, поэтому убедитесь, что вы включили поддержку Kotlin.
3. Использование службы преобразования документов
Мы будем использовать сервис преобразования документов Watson для преобразования веб-страниц HTML в простой текст. Чтобы разрешить пользователю вводить адрес веб-страницы, добавьте виджет EditText
в макет своей деятельности. Кроме того, TextView
виджет TextView
для отображения содержимого веб-страницы в виде простого текста. Чтобы убедиться, что содержимое длинных веб-страниц не усекается, я предлагаю вам поместить их в виджет ScrollView
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
<EditText
android:layout_width=»match_parent»
android:layout_height=»wrap_content»
android:id=»@+id/documentURL»
android:inputType=»textUri»
android:hint=»URL»
android:imeOptions=»actionGo»
/>
<ScrollView
android:layout_width=»wrap_content»
android:layout_height=»match_parent»
android:layout_below=»@+id/documentURL»>
<TextView
android:layout_width=»match_parent»
android:layout_height=»match_parent»
android:id=»@+id/documentContents»
/>
</ScrollView>
|
В приведенном выше коде вы можете видеть, что атрибут imeOptions
виджета EditText
установлен в actionGo
. Это позволяет пользователям нажимать кнопку «Перейти» на своих виртуальных клавиатурах после завершения ввода адреса. Чтобы прослушать это событие нажатия кнопки, добавьте следующий код Kotlin в метод onCreate()
вашей деятельности:
1
2
3
4
5
6
7
8
|
documentURL.setOnEditorActionListener { _, action, _ ->
if (action == EditorInfo.IME_ACTION_GO) {
// More code here
}
false
}
|
В прослушивателе событий первое, что нам нужно сделать, это определить URL-адрес, введенный пользователем. Это легко сделать, обратившись к свойству text
виджета EditText
. Получив URL, мы можем использовать метод httpGet()
для загрузки содержимого веб-страницы.
Поскольку мы хотим, чтобы метод httpGet()
выполнялся асинхронно, мы должны добавить к нему обратный вызов, используя метод responseString()
, который также позволяет обрабатывать загруженное содержимое в виде строки.
1
2
3
4
5
6
7
|
val url:String = documentURL.text.toString()
url.httpGet().responseString { _, _, result ->
val (document, _) = result
if (err == null) {
// More code here
}
}
|
Теперь пришло время создать экземпляр класса DocumentConversion
, который имеет все методы, которые нам нужны для взаимодействия со службой преобразования документов. Его конструктор ожидает дату версии вместе с учетными данными службы.
1
2
3
4
5
|
val documentConverter = DocumentConversion(
DocumentConversion.VERSION_DATE_2015_12_01,
resources.getString(R.string.document_conversion_username),
resources.getString(R.string.document_conversion_password)
)
|
Watson Java SDK не позволяет нам напрямую передавать строки в службу преобразования документов. Вместо этого ему нужны объекты File
. Поэтому давайте теперь создадим временный файл, используя метод createTempFile()
класса File
, и запишем в него содержимое загруженной веб-страницы, используя метод writeText()
.
1
2
|
val tempFile = File.createTempFile(«temp_file», null)
tempFile.writeText(document, Charsets.UTF_8)
|
На этом этапе мы можем вызвать метод convertDocumentToText()
и передать ему временный файл, чтобы начать преобразование. Метод также ожидает MIME-тип временного файла, поэтому не забудьте включить его. После завершения преобразования вы можете отобразить простой текст, просто назначив его text
свойству виджета TextView
.
Следующий код показывает, как выполнить преобразование в новом потоке и обновить TextView
в потоке пользовательского интерфейса:
1
2
3
4
5
6
7
8
|
AsyncTask.execute {
val plainText = documentConverter
.convertDocumentToText(tempFile, «text/html»)
.execute()
runOnUiThread {
documentContents.text = plainText
}
}
|
Вы можете запустить приложение сейчас и ввести URL-адрес немецкой веб-страницы, чтобы увидеть, как работает служба преобразования документов.
4. Использование службы языкового переводчика
С сервисом Language Translator мы теперь преобразуем простой текст на немецком языке в английский.
Вместо того, чтобы обновлять наш макет, чтобы позволить пользователю вручную запустить перевод, давайте добавим меню в нашу деятельность. Для этого создайте новый файл ресурсов меню и добавьте в него следующий код:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
<?xml version=»1.0″ encoding=»utf-8″?>
<menu xmlns:android=»http://schemas.android.com/apk/res/android»
xmlns:app=»http://schemas.android.com/apk/res-auto»>
<item android:id=»@+id/action_translate»
android:title=»Translate»
app:showAsAction = «never» />
<item android:id=»@+id/action_analyze»
android:title=»Analyze»
app:showAsAction = «never» />
</menu>
|
Как видите, приведенный выше код создает меню с двумя параметрами: переводить и анализировать. На этом этапе мы будем работать только с первым вариантом.
Чтобы отобразить меню, мы должны надуть его внутри onCreateOptionsMenu()
нашей деятельности.
1
2
3
4
|
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.my_menu, menu)
return super.onCreateOptionsMenu(menu)
}
|
Переопределив метод onOptionsItemSelected()
, мы можем узнать, когда пользователь использует меню. Кроме того, мы можем определить, какой элемент пользователь нажал, проверив itemId
. Следующий код проверяет, выбрал ли пользователь опцию перевода.
1
2
3
4
5
6
|
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
if(item?.itemId == R.id.action_translate) {
// More code here
}
return true
}
|
Как и служба документов, служба языковых переводчиков также имеет специальный класс, который позволяет нам взаимодействовать с ним. Как вы уже догадались, он называется LanguageTranslator
. Чтобы создать экземпляр класса, нам нужно передать только конструктор учетные данные службы входа в систему.
1
2
3
4
|
val translator = LanguageTranslator(
resources.getString(R.string.language_translator_username),
resources.getString(R.string.language_translator_password)
)
|
В классе есть метод translate()
который мы теперь можем использовать для перевода нашего немецкого текста на английский. В качестве аргументов он ожидает перевода текста в виде строки, текущего языка текста и желаемого языка.
После успешного завершения перевода у нас будет доступ к объекту TranslationResult
, свойство firstTranslation
которого содержит переведенный текст.
В следующем коде показано, как выполнить перевод и отобразить результат в виджете TextView
.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
AsyncTask.execute {
val translatedDocument = translator
.translate(
documentContents.text
.toString(),
Language.GERMAN,
Language.ENGLISH
).execute()
runOnUiThread {
documentContents.text = translatedDocument
.firstTranslation
}
}
|
Теперь вы можете снова запустить приложение, ввести URL-адрес немецкой веб-страницы и использовать меню для перевода его содержимого на английский язык.
5. Использование службы понимания естественного языка
Наконец, чтобы выполнить семантический анализ переведенного текста и извлечь из него различные важные детали, мы можем использовать класс NaturalLanguageUnderstanding
, который служит клиентом для службы понимания естественного языка.
Следующий код показывает, как инициализировать клиент, только когда пользователь нажимает второй вариант меню, которое мы создали на предыдущем шаге:
01
02
03
04
05
06
07
08
09
10
11
|
if(item?.itemId == R.id.action_analyze) {
val analyzer = NaturalLanguageUnderstanding(
NaturalLanguageUnderstanding.VERSION_DATE_2017_02_27,
resources.getString(
R.string.natural_language_understanding_username),
resources.getString(
R.string.natural_language_understanding_password)
)
// More code here
}
|
По сравнению с другими службами, относящимися к естественному языку, использование службы понимания естественного языка несколько сложнее, в первую очередь потому, что она имеет большое количество функций.
А пока, скажем, мы хотим определить общее настроение переведенного текста и извлечь все основные сущности, которые он упоминает. С каждой сущностью могут быть связаны эмоции и чувства, поэтому предположим, что мы тоже хотим извлечь их.
Чтобы сообщить службе, что мы хотим извлечь все сущности и связанные с ними эмоции и чувства, нам нужен объект EntitiesOptions
, который можно создать с помощью класса EntitiesOptions.Builder
.
1
2
3
4
|
val entityOptions = EntitiesOptions.Builder()
.emotion(true)
.sentiment(true)
.build()
|
Точно так же, чтобы сообщить сервису, что мы хотим общего настроения текста, нам нужен объект SentimentOptions
.
1
2
3
|
val sentimentOptions = SentimentOptions.Builder()
.document(true)
.build()
|
Объекты SentimentOptions
и EntitiesOptions
теперь необходимо связать вместе, чтобы сформировать объект Features
, который можно использовать для создания объекта AnalyzeOptions
. Объект AnalyzeOptions
является наиболее важным из всех вышеперечисленных объектов, поскольку именно здесь вы указываете текст, который хотите проанализировать.
1
2
3
4
5
6
7
8
9
|
val features = Features.Builder()
.entities(entityOptions)
.sentiment(sentimentOptions)
.build()
val analyzerOptions = AnalyzeOptions.Builder()
.text(documentContents.text.toString())
.features(features)
.build()
|
Когда объект AnalyzeOptions
готов, мы можем передать ему метод analysis analyze()
чтобы начать анализ.
1
2
3
4
5
|
AsyncTask.execute {
val results = analyzer.analyze(analyzerOptions).execute()
// More code here
}
|
Результатом анализа является объект AnalysisResults
, содержащий всю запрашиваемую нами информацию.
Чтобы определить общее настроение текста, мы должны сначала извлечь общую оценку настроения, используя свойство sentiment.document.score
. Оценка настроения — не что иное, как число с плавающей запятой. Если это ноль, настроение нейтральное. Если оно отрицательное или положительное, то настроение тоже отрицательное или положительное.
1
2
3
4
5
6
7
8
|
val overallSentimentScore = results.sentiment.document.score
var overallSentiment = «Positive»
if(overallSentimentScore < 0.0)
overallSentiment = «Negative»
if(overallSentimentScore == 0.0)
overallSentiment = «Neutral»
var output = «Overall sentiment: ${overallSentiment}\n\n»
|
Затем, просматривая список entities
в объекте AnalysisResults
, мы можем обрабатывать каждую сущность по отдельности. По умолчанию с каждой сущностью связан тип. Например, служба может сказать, является ли организация лицом, компанией или транспортным средством. В настоящее время он может идентифицировать более 450 различных типов объектов.
Поскольку мы просили их, у каждой сущности теперь также будет оценка настроения и эмоции, связанные с ней.
Мы можем определить оценку настроения, просто используя свойство sentiment.score
. Однако определить эмоцию, связанную с сущностью, не так просто. Ватсон в настоящее время поддерживает пять эмоций: гнев, радость, отвращение, страх и грусть. У каждой сущности будут все пять эмоций, но разные ценности связаны с каждой из них, определяя, насколько служба уверена, что эмоция правильная. Поэтому, чтобы определить правильные эмоции, мы должны выбрать ту, которая имеет наибольшее значение.
Следующий код перечисляет каждую сущность вместе с ее типом, оценкой настроения и эмоциями:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
for(entity in results.entities) {
output += «${entity.text} (${entity.type})\n»
val validEmotions = arrayOf(«Anger», «Joy», «Disgust»,
«Fear», «Sadness»)
val emotionValues = arrayOf(
entity.emotion.anger,
entity.emotion.joy,
entity.emotion.disgust,
entity.emotion.fear,
entity.emotion.sadness
)
val currentEmotion = validEmotions[
emotionValues.indexOf(
emotionValues.max()
)
]
output += «Emotion: ${currentEmotion}, » +
«Sentiment: ${entity.sentiment.score}» +
«\n\n»
}
|
Чтобы отобразить сгенерированный нами вывод, мы можем снова обновить виджет TextView
.
1
2
3
|
runOnUiThread {
documentContents.text = output
}
|
На этом этапе вы можете снова запустить приложение, чтобы увидеть, как все три службы работают вместе.
Вывод
Теперь вы знаете, как пользоваться тремя наиболее широко используемыми услугами, связанными с естественным языком, которые предлагает Watson. В этом руководстве вы также увидели, как легко использовать Java SDK Watson, чтобы все службы работали вместе для создания умного приложения для Android.
Чтобы узнать больше об услугах и SDK, вы можете обратиться к репозиторию GitHub SDK. А чтобы узнать больше об использовании машинного обучения Watson в своих собственных приложениях, ознакомьтесь с некоторыми другими нашими статьями здесь на Envato Tuts +!