近期在学习IBM全栈应用开发微学士课程,故此记录学习笔记。

1. Python编码实践和打包概念

1.1. 开发生命周期

应用程序开发生命周期分为七个阶段,包括:

  • 收集需求: 收集应用程序的用户、业务和技术需求。
  • 分析: 分析需求。
  • 设计: 设计完整的解决方案。
  • 编码和测试: 构建和测试应用程序的不同组件。
  • 用户和系统测试: 用户测试应用程序的可用性,进行系统集成测试和性能测试。
  • 生产: 应用程序可供所有最终用户使用。
  • 维护: 升级或修复任何用户或系统问题。

1.2. PEP8

PEP8 关于代码可读性的指导原则包括以下内容:

  • 缩进四个空格。
  • 空行用于分隔函数和类。
  • 操作符周围和逗号后的空格。

PEP8 的编码规范具有一致性和可管理性,其中包括:

  • 在函数内添加较大的代码块。
  • 使用带下划线的小写字母命名函数和文件。
  • 使用驼峰大写为类命名。
  • 用大写字母命名常量,单词之间用下划线分隔。

1.3. 单元测试

单元测试是一种验证代码单元是否按设计运行的方法。在与最终代码库集成之前,必须测试每个单元。

1.3.1. 例子

1
2
3
4
5
def add(a,b):
return a + b

def substract(a,b):
return a - b
1
2
3
4
5
6
7
8
9
import unittest
from math import add, substract

class TestMain(unittest.TestCase):
def test_add(self):
self.assertEqual(add(6,4),10)

def test_substract(self):
self.assertEqual(substract(6,4),3)

1.4. 创建包

  1. 以软件包名称创建文件夹。
  2. 创建一个空的 __init__.py 文件。
  3. 创建所需的模块。
  4. __init__.py 文件中,添加代码以引用软件包中需要的模块。

可以在 Python shell 中通过 bash 终端验证软件包。

1.5. 其他

关于网络应用程序,以下两种说法是正确的:

所有网络应用程序都是 API。

网络应用支持 CRUD 操作。

在哪个测试阶段验证应用程序在更大框架内的功能?:

集成测试。

PyLint 是一款 Python 静态代码分析工具。

在为一个管理活动的应用程序收集需求时,客户提到项目的目标是提高客户保留率。
业务需求描述了上述情况。

2. 使用Flask部署网络应用程序

2.1. Flask

Flask 是一个微型框架,只需最少的依赖即可运行。Flask 具有调试服务器、路由、模板和错误处理等功能,可用于构建网站。Flask 可以作为 python 软件包安装。与 Flask 相比,Django 是一个全栈框架。你可以通过实例化 Flask 类来创建服务器。

Flask 为每次客户端调用提供一个请求和一个响应对象。可以从 Flask 请求中获取更多信息,如标题。您可以解析请求对象,获取查询参数、正文和其他参数。您甚至可以在将响应发送回客户端之前,在响应对象上设置状态。

可以使用动态路由创建 RESTful 端点。

HTTP 状态代码有多种类型,分别显示成功、用户错误或服务器错误。Flask 在响应时会隐式返回成功代码 200。您也可以明确提供状态代码。Flask 还提供应用程序级的错误处理程序。

Flask 支持 CRUD。

可以使用 Flask 渲染静态和动态模板。

2.1.1. 代码案例

实例化 Flask:

1
2
3
from flask import Flask

app = Flask(__name__)

@app.route装饰器:

1
2
3
@app.route('/')
def hello_world():
return "<b>My first Flask application in action!</b>"

400 错误:

1
2
3
4
5
6
7
8
9
10
@app.route('/')
def search_response():
query = request.args.get('q')
if not query:
return {"error_message": "Input parameter missing"}, 422
resource = fetch_from_database(query)
if resource:
return {"message": resource}
else:
return {"error_message": "Resource not found"}, 404

3. 使用Flask创建人工智能应用并进行部署

Watson NLP 库被嵌入到实验网页中,外部工具无法访问。

3.1. 项目概述

在这个同行评分的毕业设计中,你将扮演一名软件工程师,需要开发一款基于人工智能的网络应用程序。您将分析情景并执行以下任务:

  1. 克隆项目资源库。
  2. 使用 Watson NLP 库创建一个情绪检测应用程序。
  3. 格式化应用程序的输出。
  4. 打包应用程序。
  5. 在应用程序上运行单元测试。
  6. 使用 Flask 对应用程序进行网络部署。
  7. 纳入错误处理。
  8. 运行静态代码分析。

在这个项目中,您将开发一个集成了 Embeddable Watson 人工智能库的网络应用程序。然后,您将提交相关截图供同行评审。

3.2. 情景

你被一家电子商务公司聘为软件工程师,负责创建一个基于人工智能的网络应用程序,对其特色产品的客户反馈进行分析。为实现这一要求,您将创建一个情感检测系统,处理客户以文本格式提供的反馈,并解读相关的情感表达。

情感检测扩展了情感分析的概念,从语句中提取更细微的情感,如喜悦、悲伤、愤怒等,而不是情感分析所提供的简单极性。这使得情感检测成为一个非常重要的研究分支,企业将此类系统广泛用于基于人工智能的推荐系统、自动聊天机器人等。

3.2.1. 详情

Watson NLP 库是预装在 Cloud IDE 框架中的可嵌入库。因此,无需将它们导入到你的代码中。你只需向库中的正确函数发送post请求,然后接收输出。

这些可嵌入的人工智能库基于流行的人工智能模型,提供了各种基于 NLP 和语音的功能。

使用 Watson NLP 库的 Emotion Predict 方法。访问该功能的 URL、Headers 和 Input json 格式如下。

1
2
3
URL: 'https://sn-watson-emotion.labs.skills.network/v1/watson.runtime.nlp.v1/NlpService/EmotionPredict'
Headers: {"grpc-metadata-mm-model-id": "emotion_aggregated-workflow_lang_en_stock"}
Input json: { "raw_document": { "text": text_to_analyse } }

text_to_analyze 被用作一个变量,用于保存需要分析的实际书面文本。

  1. 在项目根目录创建 emotion_detection.py 文件。

  2. 创建 emotion_detector 方法用于运行情感检测。

    • 用于分析的文本会被存储在 text_to_analyze 变量中,并会被作为参数传入该方法内。
    1
    2
    3
    4
    5
    6
    7
    8
    import requests

    def emotion_detector(text_to_analyse: str):
    URL = 'https://sn-watson-emotion.labs.skills.network/v1/watson.runtime.nlp.v1/NlpService/EmotionPredict'
    Headers = { "grpc-metadata-mm-model-id": "emotion_aggregated-workflow_lang_en_stock" }
    Input_Json = { "raw_document": { "text": text_to_analyse } }
    response = requests.post(url=URL, json=Input_Json, headers=Headers)
    return response.json()

    返回的内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    {
    "emotionPredictions": [
    {
    "emotion": {
    "anger": 0.0132405795,
    "disgust": 0.0020517302,
    "fear": 0.009090992,
    "joy": 0.9699522,
    "sadness": 0.054984167
    },
    "target": "",
    "emotionMentions": [
    {
    "span": {
    "begin": 0,
    "end": 26,
    "text": "i love this new technology"
    },
    "emotion": {
    "anger": 0.0132405795,
    "disgust": 0.0020517302,
    "fear": 0.009090992,
    "joy": 0.9699522,
    "sadness": 0.054984167
    }
    }
    ]
    }
    ],
    "producerId": {
    "name": "Ensemble Aggregated Emotion Workflow",
    "version": "0.0.1"
    }
    }
  3. 调整输出,使其返回以下格式的内容:

    1
    2
    3
    4
    5
    6
    7
    8
    {
    "anger": anger_score,
    "disgust": disgust_score,
    "fear": fear_score,
    "joy": joy_score,
    "sadness": sadness_score,
    "dominant_emotion": "<name of dominant emotion>"
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def emotion_detector(text_to_analyse: str):
    URL = 'https://sn-watson-emotion.labs.skills.network/v1/watson.runtime.nlp.v1/NlpService/EmotionPredict'
    Headers = { "grpc-metadata-mm-model-id": "emotion_aggregated-workflow_lang_en_stock" }
    Input_Json = { "raw_document": { "text": text_to_analyse } }
    response = requests.post(url=URL, json=Input_Json, headers=Headers)
    emotions = response.json()['emotionPredictions'][0]['emotion']
    items = emotions.items()
    max_item = max(items, key=lambda x: x[1])
    emotions['dominant_emotion'] = max_item[0]
    return emotions
  4. 创建 EmotionDetection Python 包。

    1. 创建文件夹,命名为 EmotionDetection
    2. 创建 __init__.py 文件。
    3. 在包的根目录下创建 emotion_detector.py 文件,其中包含我们之前创建的 emotion_detector 函数。
  5. 在应用程序上运行单元测试。

    1. 创建新文件 test_emotion_detection.py,调用包中的函数,并针对以下语句和主要情绪进行测试:
      语句 主要情绪
      I am glad this happened joy
      I am really mad about this anger
      I feel disgusted just hearing about this disgust
      I am so sad about this sadness
      I am really afraid that this will happen fear
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      import pytest
      from EmotionDetection import emotion_detector

      def test_emotion_detector():
      test_cases = [
      ("I am glad this happened", "joy"),
      ("I am really mad about this", "anger"),
      ("I feel disgusted just hearing about this", "disgust"),
      ("I am so sad about this", "sadness"),
      ("I am really afraid that this will happen", "fear"),
      ]

      for text, expected_emotion in test_cases:
      result = emotion_detector(text)
      assert result['dominant_emotion'] == expected_emotion, f"For text: {text}, expected: {expected_emotion}, but got: {result['dominant_emotion']}"

      if __name__ == "__main__":
      pytest.main()
    2. 检查单元测试输出,验证单元测试是否通过。
      1
      python3.11 test_emotion_detection.py

使用 Flask 进行应用程序的网络部署:

注意:模板文件夹中的 index.html 文件和静态文件夹中的 mywebscript.js 文件已作为仓库的一部分提供。本项目无需对这些文件进行改动。

  1. 在项目根目录下创建 server.py。确保应用程序调用函数的 Flask 装饰器是 \emotionDetector。客户要求以下面示例中的格式显示输出结果(假设是对 "I love my life" 这句话进行评价):
    1
    2
    3
    4
    5
    6
    7
    8
    {
    "anger": 0.006274985,
    "disgust": 0.0025598293,
    "fear": 0.009251528,
    "joy": 0.9680386,
    "sadness": 0.049744144,
    "dominant_emotion":"joy"
    }
    响应应当被格式化为:
    1
    For the given statement, the system response is 'anger': 0.006274985, 'disgust': 0.0025598293, 'fear': 0.009251528, 'joy': 0.9680386 and 'sadness': 0.049744144. The dominant emotion is joy.
    应用程序需要部署在 localhost:5000 上。
  2. 在函数 emotion_detector 中加入错误处理功能,以管理用户的空白条目,即在没有任何输入的情况下运行应用程序。
    • 访问服务器响应的 status_code 属性,以正确显示空白条目的系统响应。
    • 对于 status_code = 400,函数将返回相同的字典,但所有键的值均为 None
  3. 修改 server.py,在 dominant_emotionNone 时加入错误处理。在这种情况下,响应应显示 Invalid text! Please try again!.

EmotionDetection/__init__.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests

def emotion_detector(text_to_analyse: str):
if not text_to_analyse:
return {'anger': None, 'disgust': None, 'fear': None, 'joy': None, 'sadness': None, 'dominant_emotion': None}

URL = 'https://sn-watson-emotion.labs.skills.network/v1/watson.runtime.nlp.v1/NlpService/EmotionPredict'
Headers = { "grpc-metadata-mm-model-id": "emotion_aggregated-workflow_lang_en_stock" }
Input_Json = { "raw_document": { "text": text_to_analyse } }
response = requests.post(url=URL, json=Input_Json, headers=Headers)
if response.status_code == 400:
return {'anger': None, 'disgust': None, 'fear': None, 'joy': None, 'sadness': None, 'dominant_emotion': None}

emotions = response.json()['emotionPredictions'][0]['emotion']
items = emotions.items()
max_item = max(items, key=lambda x: x[1])
emotions['dominant_emotion'] = max_item[0]
return emotions

server.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
"""
This module defines a Flask application that provides an emotion detection API.
"""

from flask import Flask, request, render_template
from EmotionDetection import emotion_detector

app = Flask(__name__)

@app.route('/emotionDetector')
def handle_emotion_detector():
"""
This function handles GET requests to the /emotionDetector route. It
expects a textToAnalyze arg that contains the text to analyze. It returns
a string with all predicted emotions.
"""
predicted_emotions = emotion_detector(request.args.get('textToAnalyze'))
if predicted_emotions['dominant_emotion'] is None:
return 'Invalid text! Please try again!'

response_text = ("For the given statement, " +
f"the system response is 'anger': {predicted_emotions['anger']}, "
f"'disgust': {predicted_emotions['disgust']}, " +
f"'fear': {predicted_emotions['fear']}, "
f"'joy': {predicted_emotions['joy']} " +
f"and 'sadness': {predicted_emotions['sadness']}. "
f"The dominant emotion is {predicted_emotions['dominant_emotion']}.")
return response_text

@app.route("/")
def render_index_page():
"""
This function renders index.html to the / route.
"""
return render_template('index.html')

if __name__ == '__main__':
app.run(host='localhost', port=5000)