diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml new file mode 100644 index 0000000000..3d479c642c --- /dev/null +++ b/.github/workflows/ruby.yml @@ -0,0 +1,38 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake +# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby + +name: Ruby + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +permissions: + contents: read + +jobs: + test: + + runs-on: ubuntu-latest + strategy: + matrix: + ruby-version: ['2.6', '2.7', '3.0'] + + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, + # change this to (see https://github.com/ruby/setup-ruby#versioning): + # uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 + with: + ruby-version: ${{ matrix.ruby-version }} + bundler-cache: true # runs 'bundle install' and caches installed gems automatically + - name: Run tests + run: bundle exec rake diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Cache/b7/9606fb3afea5bd1609ed40b622142f1c98125abcfe89a76a661b0e8e343910 b/.jekyll-cache/Jekyll/Cache/Jekyll--Cache/b7/9606fb3afea5bd1609ed40b622142f1c98125abcfe89a76a661b0e8e343910 new file mode 100644 index 0000000000..0f5cadf4ea --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Cache/b7/9606fb3afea5bd1609ed40b622142f1c98125abcfe89a76a661b0e8e343910 @@ -0,0 +1 @@ +I") {"source"=>"D:/Documents/GitHub/ChanJeunlam.github.io", "destination"=>"D:/Documents/GitHub/ChanJeunlam.github.io/_site", "collections_dir"=>"", "cache_dir"=>".jekyll-cache", "plugins_dir"=>"_plugins", "layouts_dir"=>"_layouts", "data_dir"=>"_data", "includes_dir"=>"_includes", "collections"=>{"posts"=>{"output"=>true, "permalink"=>"/:year/:month/:title/"}}, "safe"=>false, "include"=>[".htaccess"], "exclude"=>[".sass-cache", ".jekyll-cache", "gemfiles", "Gemfile", "Gemfile.lock", "node_modules", "vendor/bundle/", "vendor/cache/", "vendor/gems/", "vendor/ruby/"], "keep_files"=>[".git", ".svn"], "encoding"=>"utf-8", "markdown_ext"=>"markdown,mkdown,mkdn,mkd,md", "strict_front_matter"=>false, "show_drafts"=>nil, "limit_posts"=>0, "future"=>false, "unpublished"=>false, "whitelist"=>[], "plugins"=>["jekyll-paginate"], "markdown"=>"kramdown", "highlighter"=>"rouge", "lsi"=>false, "excerpt_separator"=>"\n\n", "incremental"=>false, "detach"=>false, "port"=>"4000", "host"=>"127.0.0.1", "baseurl"=>nil, "show_dir_listing"=>false, "permalink"=>"/:year/:month/:title/", "paginate_path"=>"page/:num/", "timezone"=>nil, "quiet"=>false, "verbose"=>false, "defaults"=>[], "liquid"=>{"error_mode"=>"warn", "strict_filters"=>false, "strict_variables"=>false}, "kramdown"=>{"auto_ids"=>true, "toc_levels"=>[1, 2, 3, 4, 5, 6], "entity_output"=>"as_char", "smart_quotes"=>"lsquo,rsquo,ldquo,rdquo", "input"=>"GFM", "hard_wrap"=>false, "guess_lang"=>true, "footnote_nr"=>1, "show_warnings"=>false, "math_engine"=>"mathjax", "syntax_highlighter"=>"rouge", "syntax_highlighter_opts"=>{"disable"=>true}}, "title"=>"陈同学", "subtitle"=>"个人站", "description"=>"欢迎来到我的个人站~", "avatarTitle"=>"nothing can stop me", "avatarDesc"=>"物理海洋学在读", "url"=>"http://localhost:4000", "enableToc"=>true, "social"=>{"mail"=>"jchenfl@connect.ust.hk"}, "textColor"=>nil, "cover_color"=>"clear", "blog_button"=>{"title"=>"博客主页"}, "nav"=>[{"title"=>"所有文章", "description"=>"archive", "url"=>"/archive"}, {"title"=>"标签", "description"=>"tags", "url"=>"/tags"}, {"title"=>"技术支持", "description"=>"support", "url"=>"/support"}, {"title"=>"关于我", "description"=>"about", "url"=>"/about"}], "gems"=>["jekyll-paginate", "jekyll-sitemap"], "paginate"=>20, "livereload_port"=>35729, "serving"=>true, "watch"=>true}:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/01/b5e0fc46fc760629704bd030cb0cedf5328d21c7cebd2e6ebb1815ef170054 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/01/b5e0fc46fc760629704bd030cb0cedf5328d21c7cebd2e6ebb1815ef170054 new file mode 100644 index 0000000000..3f8211c6ac --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/01/b5e0fc46fc760629704bd030cb0cedf5328d21c7cebd2e6ebb1815ef170054 @@ -0,0 +1,28 @@ +I"b + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/01/df8ce918bac1637ac6268843d1241dd157698ead997f3f1e0c56aa64ea10df b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/01/df8ce918bac1637ac6268843d1241dd157698ead997f3f1e0c56aa64ea10df new file mode 100644 index 0000000000..2a098b2e63 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/01/df8ce918bac1637ac6268843d1241dd157698ead997f3f1e0c56aa64ea10df @@ -0,0 +1,9 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/03/583b7404770fa0fb0ca0bf3f03c507f3e7ce34dbde75144fa23f0face11849 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/03/583b7404770fa0fb0ca0bf3f03c507f3e7ce34dbde75144fa23f0face11849 new file mode 100644 index 0000000000..84c8919d5f --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/03/583b7404770fa0fb0ca0bf3f03c507f3e7ce34dbde75144fa23f0face11849 @@ -0,0 +1,23 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/03/e999d53c2d290434c2ec301bc6a9e45c424054cb5eac5e5dbcf253b8b9a4e5 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/03/e999d53c2d290434c2ec301bc6a9e45c424054cb5eac5e5dbcf253b8b9a4e5 new file mode 100644 index 0000000000..8002cf7c85 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/03/e999d53c2d290434c2ec301bc6a9e45c424054cb5eac5e5dbcf253b8b9a4e5 @@ -0,0 +1,2 @@ +I"3

谢春花的歌

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/04/98b4e8c3cb7957bf7a635202eb65bc47ac42d38b9de4203acf632b99585e5a b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/04/98b4e8c3cb7957bf7a635202eb65bc47ac42d38b9de4203acf632b99585e5a new file mode 100644 index 0000000000..33e9e3e7df --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/04/98b4e8c3cb7957bf7a635202eb65bc47ac42d38b9de4203acf632b99585e5a @@ -0,0 +1,2 @@ +I"E

02 概率论与信息论

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/05/eb8f68557b04f181c1f76f54c1682ddfd8b3185805aae395f81efca906e840 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/05/eb8f68557b04f181c1f76f54c1682ddfd8b3185805aae395f81efca906e840 new file mode 100644 index 0000000000..1e82484597 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/05/eb8f68557b04f181c1f76f54c1682ddfd8b3185805aae395f81efca906e840 @@ -0,0 +1,236 @@ +I"9

BDD接口测试是什么?

+ +

BDD (Behavior Driven Development) 是一种软件开发方法,旨在通过定义和执行行为来增强软件的可理解性和可维护性。在接口测试中,BDD 可以帮助我们更好地描述接口的行为和期望结果,从而提高测试用例的可读性和可维护性。

+ +

Pytest-BDD 是一个 Pytest 插件,它提供了对 BDD 测试的支持。下面是一些使用 Pytest-BDD 进行接口测试的步骤:

+ +
    +
  1. 安装 Pytest-BDD
  2. +
+ +

可以使用 pip 命令安装 Pytest-BDD:

+ +
pip install pytest-bdd
+
+ +
    +
  1. 定义 Feature 文件
  2. +
+ +

在 BDD 中,测试用例通常以 Feature 文件的形式进行组织。Feature 文件是一个文本文件,用于描述测试用例的场景和期望结果。例如,以下是一个示例 Feature 文件:

+ +
Feature: User API
+
+Scenario: Create a new user
+    Given I have a valid access token
+    When I make a POST request to "/users" with the following data
+        | username | john_doe |
+        | email    | john_doe@example.com |
+        | password | 123456 |
+    Then the response status code should be 201
+    And the response should contain the following data
+        | username | john_doe |
+        | email    | john_doe@example.com |
+
+ +

在这个示例中,Feature 文件定义了一个名为 “User API” 的 Feature,其中包含一个名为 “Create a new user” 的 Scenario。Scenario 中描述了测试用例的场景和期望结果,包括给定的前提条件 (Given)、操作 (When) 和断言 (Then)。

+ +
    +
  1. 定义 Step Definitions
  2. +
+ +

Step Definitions 是一个 Python 模块,用于实现 Feature 文件中定义的 Given、When 和 Then 步骤。例如,以下是一个示例 Step Definitions:

+ +
import requests
+from pytest_bdd import given, when, then, parsers
+
+@given("I have a valid access token")
+def valid_access_token():
+    # 返回一个有效的访问令牌
+    return "valid_token"
+
+@when(parsers.parse('I make a {method} request to "{url}" with the following data'))
+def make_request(method, url):
+    # 根据方法和 URL 发送请求
+    # 将数据作为请求正文发送
+    response = requests.request(method, url, json=parsers.parse_table())
+    return response
+
+@then(parsers.parse('the response status code should be {status_code:d}'))
+def check_status_code(make_request, status_code):
+    assert make_request.status_code == status_code
+
+@then(parsers.parse('the response should contain the following data'))
+def check_response_data(make_request):
+    expected_data = parsers.parse_table()
+    actual_data = make_request.json()
+    assert expected_data in actual_data.values()
+
+ +

在这个示例中,Step Definitions 实现了 Feature 文件中定义的 Given、When 和 Then 步骤。Step Definitions 使用 Pytest-BDD 提供的装饰器 (如 @given、@when 和 @then) 来标记步骤,以及 Pytest-BDD 提供的 parsers.parse 方法来解析表格数据。

+ +
    +
  1. 运行测试用例
  2. +
+ +

使用 Pytest 运行测试用例时,可以使用 --bdd 参数来指定测试目录和模式,如:

+ +
pytest --bdd tests/ --verbose
+
+ +

在运行测试用例时,Pytest-BDD 将自动扫描测试目录下的 Feature 文件和 Step Definitions,然后根据 Feature 文件定义的场景执行测试用例。

+ +

总之,使用 Pytest-BDD 进行接口测试可以帮助我们更好地描述接口的行为和期望结果,从而提高测试用例的可读性和可维护性。建议了解 Pytest-BDD 官方文档中有关 BDD 测试的更多内容,以便更好地使用 Pytest-BDD 进行接口测试。

+ +

定义 Feature 文件的格式可以展开说明一下吗

+ +

Feature 文件是 BDD 测试中用于描述测试用例的场景和期望结果的文本文件。它通常采用 Gherkin 语言进行编写,Gherkin 语言是一种基于自然语言的 DSL (Domain Specific Language),它提供了一种简单易懂的语法,用于描述测试用例的场景和期望结果。

+ +

下面是一个 Feature 文件的示例,它描述了一个用户管理系统的测试用例场景:

+ +
Feature: User Management
+    As a system administrator
+    I want to manage users
+    So that I can manage access to the system
+
+    Scenario: Create a new user
+        Given I am logged in as an administrator
+        When I create a new user with the following details
+            | username | john_doe |
+            | email    | john_doe@example.com |
+            | password | 123456 |
+        Then the user should be created successfully
+
+    Scenario: Update an existing user
+        Given I am logged in as an administrator
+        And there is an existing user with the username "jane_doe"
+        When I update the user with the username "jane_doe" with the following details
+            | email    | jane_doe@example.com |
+            | password | 654321 |
+        Then the user should be updated successfully
+
+    Scenario: Delete an existing user
+        Given I am logged in as an administrator
+        And there is an existing user with the username "jane_doe"
+        When I delete the user with the username "jane_doe"
+        Then the user should be deleted successfully
+
+ +

在这个示例中,Feature 文件包含了一个名为 “User Management” 的 Feature,它描述了一个用户管理系统的测试用例场景。Feature 文件中的每个 Scenario 描述了一个测试用例,包括前提条件 (Given)、操作 (When) 和断言 (Then)。

+ +

Feature 文件的基本语法格式如下:

+ +
Feature: <feature name>
+    <feature description>
+
+    Scenario: <scenario name>
+        Given <precondition>
+        When <action>
+        Then <assertion>
+
+    Scenario: <scenario name>
+        Given <precondition>
+        When <action>
+        Then <assertion>
+
+    ...
+
+ +

其中,Feature、Scenario、Given、When 和 Then 等关键字都是 Gherkin 语言中的保留关键字,用于描述测试用例的场景和期望结果。Feature 文件的详细格式和语法可以参考 Gherkin 语言的官方文档和 Pytest-BDD 的官方文档。

+ +

Step Definitions中的@when(parsers.parse(‘I make a request to “” with the following data’))我看不太懂

+ +

这段 Step Definitions 中的 @when(parsers.parse('I make a request to "{url}" with the following data')) 是一个装饰器,用于标记一个 When 步骤。该步骤对应 Feature 文件中的一行,例如:

+ +
When I make a request to "https://example.com/api/users" with the following data
+
+ +

装饰器的参数 parsers.parse('I make a request to "{url}" with the following data') 表示使用 parsers.parse() 方法来解析 Feature 文件中的文本,并将其中的 {url} 参数解析为 Step Definitions 函数的 url 参数。

+ +

当 Pytest-BDD 执行测试用例时,它会将 Feature 文件中的文本传递给 Step Definitions 函数,例如:

+ +
@when(parsers.parse('I make a request to "{url}" with the following data'))
+def make_request(url):
+    # 执行 HTTP 请求,使用 url 和数据表格中的数据
+    pass
+
+ +

在这个示例中,make_request() 函数接受一个 url 参数,并且使用该参数和数据表格中的数据来执行 HTTP 请求。例如,如果 Feature 文件中的文本是 When I make a request to "https://example.com/api/users" with the following data,则 Pytest-BDD 会将 https://example.com/api/users 作为 url 参数传递给 make_request() 函数,并且将数据表格作为函数的参数传递。

+ +

希望这样的解释能够帮助您理解 Step Definitions 中的装饰器和参数。

+ +

我看不太懂@then(parsers.parse(‘the response status code should be’)) def check_status_code(make_request, status_code): assert make_request.status_code == status_code

+ +

这段代码是一个 Step Definitions 中的 @then 装饰器,用于标记一个 Then 步骤。该步骤对应于 Feature 文件中的一行,例如:

+ +
Then the response status code should be 200
+
+ +

装饰器的参数 parsers.parse('the response status code should be {status_code:d}') 表示使用 parsers.parse() 方法来解析 Feature 文件中的文本,并将其中的 {status_code} 参数解析为 Step Definitions 函数的 status_code 参数。d 表示将该参数解析为整数类型。

+ +

当 Pytest-BDD 执行测试用例时,它会将 Feature 文件中的文本传递给 Step Definitions 函数,例如:

+ +
@then(parsers.parse('the response status code should be {status_code:d}'))
+def check_status_code(make_request, status_code):
+    assert make_request.status_code == status_code
+
+ +

在这个示例中,check_status_code() 函数接受一个 make_request 参数和一个 status_code 参数。make_request 参数是一个在之前的 Given 或 When 步骤中执行的请求对象(例如使用 requests 库发出的 HTTP 请求),而 status_code 参数则是从 Feature 文件中解析出来的 HTTP 状态码。

+ +

该函数的实现是检查 make_request 对象的状态码是否等于 status_code 参数。如果状态码不相等,那么该断言会失败,从而标记该测试用例为失败。

+ +

希望这样的解释能够帮助您理解 Step Definitions 中的装饰器和参数,并理解其在 Pytest-BDD 中的作用。

+ +

BDD接口自动化测试怎么管理很多的测试数据呢

+ +

在 BDD 接口自动化测试中,管理大量测试数据的方法有很多种,具体根据项目的实际情况和需求来选择。

+ +

以下是一些常用的管理测试数据的方法:

+ +
    +
  1. 数据库管理:将测试数据存储在数据库中,并使用 SQL 语句来获取和操作数据。这种方法可以方便地进行数据的增删改查和管理,但需要一些数据库知识和维护成本。
  2. +
  3. Excel/CSV 文件管理:将测试数据存储在 ExcelCSV 文件中,使用 Python 的 pandas 库或其他数据处理库来读取和操作数据。这种方法可以方便地进行数据的导入导出和管理,但需要一些额外的开发工作。
  4. +
  5. 配置文件管理:将测试数据存储在配置文件中,例如 YAMLJSON 格式的文件,使用 Python 的 ConfigParser 或其他配置文件解析库来读取和操作数据。这种方法可以方便地进行数据的配置和管理,但需要一些额外的开发工作。
  6. +
  7. 数据生成工具:使用 Python 的 Faker 库或其他数据生成库来生成测试数据。这种方法可以方便地生成大量的测试数据,但需要确保生成的数据符合业务逻辑和要求。
  8. +
+ +

无论采用哪种方法,都需要注意测试数据的可维护性和可重用性,以便在测试用例变更或新增时能够快速地修改或添加测试数据。同时,也需要对测试数据进行合理的组织和分类,以便于测试用例的管理和维护。

+ +

可以更加具体说一下这个步骤吗,还是不懂怎么写入大数据到feature中

+ +

如果您需要在 Feature 文件中写入大量的测试数据,可以使用数据表格来组织数据,并将数据表格写入 Feature 文件中。数据表格是 Feature 文件中以表格格式表示的数据,可以包含多行和多列的数据。

+ +

以下是一个示例 Feature 文件,其中包含一个数据表格:

+ +
Feature: 订单管理
+
+  Scenario: 创建订单
+    Given 用户登录系统
+    When 用户创建订单
+      | 商品名称 | 数量 |
+      | 商品A    | 2    |
+      | 商品B    | 1    |
+    Then 订单创建成功
+
+ +

在上面的示例中,数据表格位于 When 步骤下面,使用管道符 | 来分隔每一列的数据。第一行表示每一列的列名,后面的行表示实际的数据。您可以根据需要添加或删除列和行,以适应不同的测试场景。

+ +

在 Step Definitions 中,您可以使用 Pytest-BDD 提供的 parsers.parse_table() 方法来解析数据表格,并将其转换为 Python 中的数据结构,例如列表或字典。以下是一个示例 Step Definitions

+ +
@when(parsers.parse('用户创建订单'))
+def create_order():
+    pass
+
+@when(parsers.parse('用户创建订单'))
+def create_order(make_request, data):
+    for row in data:
+        product_name = row['商品名称']
+        quantity = row['数量']
+        # 使用商品名称和数量创建订单项
+        pass
+
+ +

在上面的示例中,create_order() 函数接受一个 make_request 参数和一个 data 参数,其中 data 参数是通过 parsers.parse_table() 方法解析出来的数据表格。在函数中,我们可以遍历数据表格的每一行,并使用其中的数据来执行一系列操作,例如创建订单项。

+ +

通过使用数据表格和 parsers.parse_table() 方法,您可以方便地将大量测试数据写入 Feature 文件和 Step Definitions 中,并且可以轻松地扩展和维护这些数据。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/06/48308429dcf44da30a98518f9713729a79a25da89bd39a5080f3afb2ece1d2 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/06/48308429dcf44da30a98518f9713729a79a25da89bd39a5080f3afb2ece1d2 new file mode 100644 index 0000000000..b31bbc41e3 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/06/48308429dcf44da30a98518f9713729a79a25da89bd39a5080f3afb2ece1d2 @@ -0,0 +1,122 @@ +I"

介绍了邮件配置的目的,邮件配置的方法,邮件封装以及邮件运行。

+ +

邮件配置

+ +

目的

+ +

使用邮件,方便获取自动化测试的结果

+ +

配置文件设置以及邮件封装

+ +

邮件配置

+ +

举个例子:

+ +

打开163邮箱或者qq邮箱的设置页面,在设置页面中找到POP3/SMTP/IMAP服务,开启服务(查看服务器地址),获取授权码,填写到配置文件config.yml中

+ +
email:
+ smtpserver: "smtp.itcast.com"
+ username: "wangxu@itcast.com"
+ password: "FDSArewq1"
+ receiver: "wangxu@itcast.com"
+
+ +

修改了配置文件后,需要在Conf.py中添加如下代码,来读取email配置有关信息:

+ +
def get_email_info(self):
+    """
+    获取邮件配置相关信息
+    :return:
+    """
+    return self.config["email"]
+
+ +

邮件封装

+ +

在utils文件夹下新建一个EmailUtil.py文件,用来封装邮件发送的方法

+ +
from email.mime.multipart import MIMEMultipart # MIMEMultipart类表示邮件的主体,可以包含多个邮件体,可以包含文本,附件等
+from email.mime.text import MIMEText # MIMEText类表示邮件的文本内容
+import smtplib
+#初始化
+#smtp地址,用户名,密码,接收邮件者,邮件标题,邮件内容,邮件附件
+class SendEmail:
+    # __init__方法表示初始化方法,当类被实例化时,会自动调用该方法
+    def __init__(self,smtp_addr,username,password,recv,
+                 title,content=None,file=None):
+        self.smtp_addr = smtp_addr # 邮件服务器地址
+        self.username = username
+        self.password = password
+        self.recv = recv
+        self.title = title
+        self.content = content
+        self.file = file
+    #发送邮件方法
+    def send_mail(self):
+        #MIME
+        msg = MIMEMultipart()
+        #初始化邮件信息
+        msg.attach(MIMEText(self.content,_charset="utf-8"))
+        msg["Subject"] = self.title
+        msg["From"] = self.username
+        msg["To"] = self.recv
+        #邮件附件
+        #判断是否附件
+        if self.file:
+        #MIMEText读取文件
+            att = MIMEText(open(self.file).read())
+        #设置内容类型
+            att["Content-Type"] = 'application/octet-stream'
+        #设置附件头
+            att["Content-Disposition"] = 'attachment;filename="%s"'%self.file
+        #将内容附加到邮件主体中
+            msg.attach(att)
+        #登录邮件服务器
+        self.smtp = smtplib.SMTP(self.smtp_addr,port=25)
+        self.smtp.login(self.username,self.password)
+    #发送邮件
+        self.smtp.sendmail(self.username,self.recv,msg.as_string())
+
+if __name__ == "__main__":
+    #初始化类(self,smtp_addr,username,password,recv,
+            #     title,content=None,file=None):
+    from config.Conf import ConfigYaml
+    email_info = ConfigYaml().get_email_info()
+    smtp_addr = email_info["smtpserver"]
+    username = email_info["username"]
+    password = email_info["password"]
+    recv = email_info["receiver"]
+    email = SendEmail(smtp_addr,username,password,recv,"测试")
+    email.send_mail()
+
+ +

邮件运行

+ +

这一步主要包括:(1)封装公共方法;(2) 应用测试发送

+ +

在common文件夹下面的Base.py文件中,添加如下代码:

+ +
def send_mail(report_html_path="",content="",title="测试"):
+    """
+    发送邮件
+    :param report_html_path:
+    :param content:
+    :param title:
+    :return:
+    """
+    email_info = ConfigYaml().get_email_info()
+    smtp_addr = email_info["smtpserver"]
+    username = email_info["username"]
+    password = email_info["password"]
+    recv = email_info["receiver"]
+    email = SendEmail(
+        smtp_addr=smtp_addr,
+        username=username,
+        password=password,
+        recv=recv,
+        title=title,
+        content=content,
+        file=report_html_path)
+    email.send_mail()
+
+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/06/64630330bf3282a4b26b683af4b4bc4b8698249aeb33fe7c12b58b3de2abfd b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/06/64630330bf3282a4b26b683af4b4bc4b8698249aeb33fe7c12b58b3de2abfd new file mode 100644 index 0000000000..76f60353c6 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/06/64630330bf3282a4b26b683af4b4bc4b8698249aeb33fe7c12b58b3de2abfd @@ -0,0 +1,6 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/07/9a88dae576eb16b57f883ca5163bfd13003aac04d2762518e1317bb6ed9d26 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/07/9a88dae576eb16b57f883ca5163bfd13003aac04d2762518e1317bb6ed9d26 new file mode 100644 index 0000000000..640009b877 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/07/9a88dae576eb16b57f883ca5163bfd13003aac04d2762518e1317bb6ed9d26 @@ -0,0 +1,2 @@ +I"E

在VScode中如何配置

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/0a/482aef90222ed651eae71b72102b333e45377092643b9154e4a2e002536866 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/0a/482aef90222ed651eae71b72102b333e45377092643b9154e4a2e002536866 new file mode 100644 index 0000000000..43122ad7af --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/0a/482aef90222ed651eae71b72102b333e45377092643b9154e4a2e002536866 @@ -0,0 +1,2 @@ +I"E

对CPU进行压力测试

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/11/86977ddc11f7b6a53917b6c8540459e9b7becbab41a6cb6fa3f85d25db70e3 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/11/86977ddc11f7b6a53917b6c8540459e9b7becbab41a6cb6fa3f85d25db70e3 new file mode 100644 index 0000000000..858fc2a633 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/11/86977ddc11f7b6a53917b6c8540459e9b7becbab41a6cb6fa3f85d25db70e3 @@ -0,0 +1,2 @@ +I"U

linux系统的网络连接状态

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/13/650f01be53f8d32fe2fcf0b74f218a0686477e3162ced221a4204e56086981 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/13/650f01be53f8d32fe2fcf0b74f218a0686477e3162ced221a4204e56086981 new file mode 100644 index 0000000000..6e81fdd991 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/13/650f01be53f8d32fe2fcf0b74f218a0686477e3162ced221a4204e56086981 @@ -0,0 +1,388 @@ +I"1

断言是一种判断程序执行结果是否符合预期的方法,可以帮助我们定位错误原因,是自动化测试的灵魂。本文主要介绍pytest中使用assert进行断言,断言的应用和封装。断言结果的验证,包括状态码、返回信息验证,以及数据库断言结果验证。

+ +

结果断言

+ +

断言介绍

+ + + +

常用断言

+ +

pytest里面的断言就是python里assert的断言方法:

+ + + +
import pytest
+
+#判断xx为真
+#1、定义方法进行assert
+def test_1():
+    a = True
+    assert a
+
+#判断XX不为真
+def test_2():
+    a= True
+    assert not a
+
+#判断b包含a
+def test_3():
+    a="hello"
+    b="hello world"
+    assert a in b
+#判断a==b
+def test_4():
+    a=b="hello"
+    assert a ==b
+
+#判断a!=b
+def test_5():
+    a="hello"
+    b="hello world"
+    assert a !=b
+
+#2、运行查看结果
+if __name__ == "__main__":
+    pytest.main(["pytest_assert.py"])
+
+ +

测试结果如下:

+
============================= test session starts ==============================
+collecting ... collected 5 items
+
+pytest_assert.py::test_1 PASSED
+pytest_assert.py::test_2 FAILED
+testcase/t_pytest/pytest_assert.py:9 (test_2)
+def test_2():
+        a= True
+>       assert not a
+E       assert not True
+
+pytest_assert.py:12: AssertionError
+
+
+
+
+pytest_assert.py::test_3 PASSED
+pytest_assert.py::test_4 PASSED
+pytest_assert.py::test_5 PASSED
+
+========================= 1 failed, 4 passed in 0.06s ==========================
+
+进程已结束,退出代码1
+
+
+

断言应用和封装

+ +

断言应用

+

首先,在utils目录下新建一个AssertUtil.py文件,用来封装断言方法。

+ +
from utils.LogUtil import my_log
+import json
+#1、定义封装类
+class AssertUtil:
+#2、初始化数据,日志
+    def __init__(self):
+        self.log = my_log("AssertUtil")
+#3、code相等
+    def assert_code(self,code,expected_code):
+        """
+        验证返回状态码
+        :param code:
+        :param expected_code:
+        :return:
+        """
+        try:
+            assert int(code) == int(expected_code)
+            return True
+        except:
+            self.log.error("code error,code is %s,expected_code is %s"%(code,expected_code))
+
+            raise
+#4、body相等
+    def assert_body(self,body,expected_body):
+        """
+        验证返回结果内容相等
+        :param body:
+        :param expected_body:
+        :return:
+        """
+        try :
+            assert body == expected_body
+            return True
+        except:
+            self.log.error("body error,body is %s,expected_body is %s"%(body,expected_body))
+            raise
+#5、body包含
+    def assert_in_body(self,body,expected_body):
+        """
+        验证返回结果是否包含期望的结果
+        :param body:
+        :param expected_body:
+        :return:
+        """
+        try:
+            body = json.dumps(body)
+            print(body)
+            assert expected_body in body
+            return True
+        except:
+            self.log.error("不包含或者body是错误,body is %s,expected_body is %s"%(body,expected_body))
+            raise
+
+ +

写好了Utils目录下的AssertUtil.py文件,接下来我们要开始在test_mall.py中调用该断言方法对返回状态码和结果进行验证。

+ +
from utils.AssertUtil import AssertUtil
+
+# 测试返回的code1
+#返回状态码
+code = r["code"]
+#assert code == 200
+AssertUtil().assert_code(code,200)
+
+#返回结果内容
+#body = json.dumps(r["body"])
+body = r["body"]
+#assert '"user_id": 1, "username": "python"' in body
+AssertUtil().assert_in_body(body,'"user_id": 1, "username": "python"')
+
+
+ +

数据库结果断言

+ +

大多数情况下,我们接口自动化测试需要对数据库进行验证,比如说,我们在注册接口中,需要验证数据库中是否有新增的数据,这时候我们就需要对数据库进行查询,然后对查询结果进行断言。比如说,我们需要对订单接口进行测试,需要验证数据库中的订单数据是否正确,这时候我们就需要对数据库进行查询,然后对查询结果进行断言。

+ +

PyMySQL安装以及简单使用

+ +

PyMySQL是Python的一个MySQL数据库驱动,它实现了Python DB API 2.0规范的全部标准,基于纯Python语言编写,所以它不依赖任何其他的库,可以很好的和Twisted,web.py等框架集成。

+ +

PyMySQL的安装非常简单,直接使用pip命令即可安装。

+ +
pip install pymysql
+
+ +

或者在requirements.txt文件中添加pymysql依赖,然后使用pip install -r requirements.txt命令安装。

+ +

pymsql的使用非常简单,下面是一个简单的例子。

+ +
import pymysql
+# 链接数据库
+conn = pymysql.connect(host= "database address",user ="user name",password="password",database="database name",charset="utf8")
+# 获取执行sql语句的光标对象
+cursor = conn.cursor() # 结果为元组
+# 获取执行SQL语句,结果作为字典返回
+cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
+# define the execute sql
+sql = "select username,password from tb_user"
+# execute sql
+cursor.exectute(sql)
+# fetchone()获取单条数据
+result = cursor.fetchone()
+# fetchall()获取所有数据
+results = cursor.fetchall()
+# 关闭光标对象
+cursor.close()
+# 关闭数据库连接
+conn.close()
+
+ +

工具类封装及使用

+ +

在utils目录下新建一个MysqlUtil.py文件,用于封装数据库操作的方法。

+ +
from utils.LogUtil import my_log
+import pymysql
+#1、创建封装类
+class Mysql:
+#2、初始化数据,连接数据库,光标对象
+    def __init__(self,host,user,password,database,charset="utf8",port=3306):
+        self.log = my_log()
+        self.conn = pymysql.connect(
+            host=host,
+            user=user,
+            password=password,
+            database=database,
+            charset=charset,
+            port=port
+            )
+        self.cursor = self.conn.cursor(cursor=pymysql.cursors.DictCursor)
+#3、创建查询、执行方法
+    def fetchone(self,sql):
+        """
+        单个查询
+        :param sql:
+        :return:
+        """
+        self.cursor.execute(sql)
+        return self.cursor.fetchone()
+
+    def fetchall(self,sql):
+        """
+        多个查询
+        :param sql:
+        :return:
+        """
+        self.cursor.execute(sql)
+        return self.cursor.fetchall()
+
+    def exec(self,sql):
+        """
+        执行
+        :return:
+        """
+        try:
+            if self.conn and self.cursor:
+                self.cursor.execute(sql)
+                self.conn.commit()
+        except Exception as ex:
+            self.conn.rollback()
+            self.log.error("Mysql 执行失败")
+            self.log.error(ex)
+            return False
+        return True
+
+#4、关闭对象
+    def __del__(self):
+        #关闭光标对象
+        if self.cursor is not None:
+            self.cursor.close()
+        #关闭连接对象
+        if self.conn is not None:
+            self.cursor.close()
+
+if __name__ == "__main__":
+    mysql = Mysql("211.103.136.242",
+                  "test",
+                  "test123456","meiduo",
+                  charset="utf8",
+                  port=7090)
+    #res = mysql.fetchall("select username,password from tb_users")
+    # 执行,修改数据库里面的数据
+    res = mysql.exec("update tb_users set first_name='python' where username = 'python'")
+    print(res)
+
+
+ +

数据库断言之配置文件

+ +

我们上一步把数据库操作封装成了一个工具类,但是在使用的时候,我们需要把数据库的连接信息写死在代码里面,这样的话,如果我们的数据库信息发生了变化,我们就需要修改代码,这样的话,我们的代码就不够灵活,所以我们需要把数据库的连接信息放到配置文件config里面,然后在代码里面读取配置文件,这样的话,我们的代码就可以灵活的修改数据库的连接信息了。

+ +

我们之前在config文件夹下面已经有了一个config.yaml文件;但是还是建议大家把数据库的连接信息放到一个单独的文件db_conf.yml里面,这样的话,我们的配置文件就更加清晰了。

+ +

步骤如下:

+ + +
#编写相关基本信息
+db_1:
+  db_host: "211.103.136.242"
+  db_user: "test"
+  db_password: "test123456"
+  db_name: "meiduo"
+  db_charset: "utf8"
+  db_port: "7090"
+
+db_2:
+  db_host: "111211.103.136.242"
+  db_user: "test"
+  db_password: "test123456"
+  db_name: "meiduo"
+  db_charset: "utf8"
+  db_port: "7090"
+
+db_3:
+  db_host: "333211.103.136.242"
+  db_user: "test"
+  db_password: "test123456"
+  db_name: "meiduo"
+  db_charset: "utf8"
+  db_port: "7090"
+
+ + +
#定义db_conf.yml路径
+_db_config_file =  _config_path + os.sep +"db_conf.yml"
+
+def get_db_config_file():
+    return _db_config_file
+
+class ConfigYaml:
+    def get_db_conf_info(self,db_alias):
+        """
+        根据db_alias获取该名称下的数据库信息
+        :param db_alias:
+        :return:
+        """
+        return self.db_config[db_alias]
+if __name__ == "__main__":
+    conf_read = ConfigYaml()
+    print(conf_read.get_db_conf_info("db_1"))
+    print(conf_read.get_db_conf_info("db_2"))
+    print(conf_read.get_db_conf_info("db_3"))
+
+
+

数据库断言之结果验证

+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/13/f60aa3105c212dec4af9c30c019d988916af3cf5d65c08b9d5a35166107fa1 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/13/f60aa3105c212dec4af9c30c019d988916af3cf5d65c08b9d5a35166107fa1 new file mode 100644 index 0000000000..ca2f6048a8 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/13/f60aa3105c212dec4af9c30c019d988916af3cf5d65c08b9d5a35166107fa1 @@ -0,0 +1,2 @@ +I"-

控制电路

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/15/9b13cba987783b711756c68e22518b77c3cc133305142156d734260daccbae b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/15/9b13cba987783b711756c68e22518b77c3cc133305142156d734260daccbae new file mode 100644 index 0000000000..f0090d196c --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/15/9b13cba987783b711756c68e22518b77c3cc133305142156d734260daccbae @@ -0,0 +1,198 @@ +I"!%

断言

+ +

断言是为了告诉开发人员程序中发生了不可恢复的错误。对于可以预料的错误(如未找到相关文件),用户可以予以纠正或重试,断言并不是为此而生的。

+ +

断言用于程序内部自检,如声明一些代码中不可能出现的条件。如果触发了某个条件,即意味着程序中存在相应的 bug。

+ +

断言语法

+ +
assert_stmt ::="assert" expression1 ["," expression2]
+
+ +

其中,expression1 是需要测试的条件,可选的 expression2 是错误消息,如果断言失败则显示该消息。

+ +

”,”表示逗号,expression1 和 expression2 之间用逗号分隔。

+ +

在执行时,Python 解释器将每条断言语句大致转换为以下这些语句:

+ +
if __debug__:
+    if not expression1:
+        raise AssertionError(expression2)
+
+ +

代码在检查断言条件之前,还会检查__debug__全局变量。这是一个内置的布尔标记,在一般情况下为真,若进行代码优化则为假。还可以使用 expression2 传递一个可选的错误消息,该消息将与回溯中的AssertionError 一起显示,用来进一步简化调试。

+ +

断言注意事项

+ +

不要使用断言验证数据

+ +

在 Python 中使用断言时要注意的一个重点是,若在命令行中使用-O 和-OO 标识,或修改CPython中的 PYTHONOPTIMIZE 环境变量,都会全局禁用断言。

+ +

不要使用断言验证数据,因为当全局禁用断言时,验证失效,可能存在严重的安全漏洞。

+ +

永不失败的断言

+ +

在将一个元组作为 assert 语句中的第一个参数传递时,断言条件总为真,因此永远不会失败。

+ +
assert(1 == 2, 'This should fail') 
+
+ +

这是因为在 Python 中非空元组总为真值。如果将元组传递给 assert 语句,则会导致断言条件始终为真,因此上述 assert 语句毫无用处,永远不会触发异常。

+ +

这也是为什么应该总是对单元测试用例先做一个快速的冒烟测试。要确保在编 +写下一个测试之前,当前测试用例的确会失败。

+ +

断言总结

+ +
    +
  1. Python 断言语句是一种测试某个条件的调试辅助功能,可作为程序的内部自检
  2. +
  3. 断言应该只用于帮助开发人员识别 bug,它不是用于处理运行时错误的机制。
  4. +
+ +

巧妙放置逗号

+ +

如果需要在 Python 中的列表、字典或集合常量中添加或移除项,记住一个窍门:在所有行后面都添加一个逗号。

+ +

如,假设代码中有如下名字组成的列表:

+ +
names = ['Alice', 'Bob', 'Cindy', 'Doug']
+
+ +

在修改这个名字列表时,通过 git diff 查看改动可能有点不方便。大多数源码控制系统都是基于行的,因此无法标出同一行中的多个改动。

+ +

一个快速改进是根据编码规范,将列表、字典或集合常量分割成多行,如下所示:

+ +
names = [
+    'Alice',
+    'Bob',
+    'Cindy'
+    'Doug'
+]
+
+ +

在列表末尾添加或移除内容时Jane,需要在’Doug’后面添加逗号,再添加或移除新的名字。否则,Python就会将字符串 Doug 和 Jane 合并成了 DougJane。这称为字符串字面值拼接,是文档中有记录的刻意行为,这种行为可能会在程序中引入令人难以琢磨的 bug。

+ +

在某些情况下,字符串字面值拼接是一个有用的特性。例如,在跨越多行的长字符串中可以省去反斜杠:

+ +
my_str = (
+    'This is a very long string that I want to split '
+    'across multiple lines.'
+)
+
+ +

但另一方面,这个特性有时又会成为负担。那么如何解决这个问题呢?

+ +

在 Python 中,可以在列表、字典和集合常量中的每一项后面都放置一个逗号,包括最后一 +项。因此只要记住在每一行末尾都加上一个逗号,就可以避免逗号放置问题。

+ +

上下文管理器和 with 语句

+ +

with 语句究竟有哪些好处?它有助于简化一些通用资源管理模式,抽象出其中的功能,将 +其分解并重用。

+ +

如使用 with 语句打开文件,不需要再手动关闭文件,因为 with 语句会在代码块执行完毕后自动关闭文件。

+ +
with open('hello.txt','w') as f:
+    f.write('hello world")
+
+ +

上面的代码如果不适用 with 语句,就需要手动关闭文件,如下所示:

+ +
f = open('hello.txt','w')
+try:
+    f.write('hello world')
+finally:
+    f.close()
+
+ +

如果只执行上述代码中的核心逻辑,那么这段代码就很简单。但是,如果在调用 f.write()时发生异常,这段代码不能保证文件最后被关闭,因此程序可能会泄露文件描述符。此时 with 语句就派上用场了,它能够简化资源的获取和释放。

+ +
f = open('hello.txt','w')
+f.write('hello world')
+f.close()
+
+ +

threading.Lock 类是 Python 标准库中另一个比较好的示例,它有效地使用了 with 语句。Lock 类的实例可以用作上下文管理器,因此可以使用 with 语句来获取和释放锁。

+ +
lock = threading.Lock()
+# have problems
+lock.acquire()
+try:
+    print('Critical section 1')
+    print('Critical section 2')
+finally:
+    lock.release()
+
+
+ +

改写代码后,使用 with 语句都可以抽象出大部分资源处理逻辑。不再需要手动调用 lock.release(),因为 with 语句会在代码块执行完毕后自动释放锁。with 语句不仅让处理系统资源的代码更易读,而且由于绝对不会忘记清理或释放资源,因此还可以避免 bug 或资源泄漏。

+ +
lock = threading.Lock()
+# have problems
+lock.acquire()
+
+# use with
+with lock:
+    print('Critical section 1')
+
+ +

在自定义对象中支持 with

+ +

只要实现所谓的上下文管理器(context manager),就可以在自定义的类和函数中获得相同的功能。

+ +

上下文管理器是什么?这是一个简单的“协议”(或接口),自定义对象需要遵循这个接口来支持 with 语句。总的来说,如果想将一个对象作为上下文管理器,需要做的就是向其中添加__enter__和__exit__方法。Python 将在资源管理周期的适当时间调用这两种方法。

+ +

下面是open()上下文管理器的实现,它是一个内置的函数,用于打开文件并返回一个文件对象。

+ +

+class OpenFile(object):
+    def __init__(self, file, mode='r'):
+        self.file = file
+        self.mode = mode
+
+    def __enter__(self):
+        self.f = open(self.file, self.mode)
+        return self.f
+    # __exit__方法的参数是异常类型、异常值和追溯信息,type、value和traceback
+    def __exit__(self, type, value, traceback):
+        self.f.close()
+
+ +

这里的OpenFile类遵循上下文管理器协议,也支持 with 语句。因此,可以使用 with 语句来打开文件,而不需要手动关闭文件。

+ +
with OpenFile('hello.txt','w') as f:
+    f.write('hello world')
+
+ +

当执行流程进入 with 语句上下文时,Python 会调用__enter__获取资源;离开 with 上下文时,Python 会调用__exit__释放资源。

+ +

使用contextlib.contextmanager装饰器,可以简化上下文管理器的实现。contextmanager装饰器会将生成器函数包装成上下文管理器。这样,就可以使用 with 语句来管理资源,而不需要实现__enter__和__exit__方法。

+ +
from contextlib import contextmanager
+
+@contextmanager
+def OpenFile(file,mode):
+    try:
+        f = open(file,mode)
+        yield f # yield 语句会在上下文管理器中暂停执行,直到离开上下文管理器
+    finally:
+        f.close()
+
+with OpenFile('hello.txt','w') as f:
+    f.write('hello world')
+
+ +

这个 managed_file()是生成器,开始先获取资源,之后暂停执行并产生资源以供调用者使用。当调用者离开 with 上下文时,生成器继续执行剩余的清理步骤,并将资源释放回系统。

+ +

总结:

+ +
    +
  1. with语句通过在所谓的上下文管理器中封装 try…finally 语句的标准用法来简化异常处理;
  2. +
  3. with 语句一般用来管理系统资源的安全获取和释放。资源首先由 with 语句获取,并在执行离开 with 上下文时自动释放;
  4. +
  5. 有效地使用 with 有助于避免资源泄漏的问题,让代码更加易于阅读。
  6. +
+ +

来源

+ +

《深入理解Python特性》P04 - P15

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/17/964e1117c07f138fb9c314ddb146a2ead39c2ae927fbab59bdae810c7b7458 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/17/964e1117c07f138fb9c314ddb146a2ead39c2ae927fbab59bdae810c7b7458 new file mode 100644 index 0000000000..dfc782f1b4 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/17/964e1117c07f138fb9c314ddb146a2ead39c2ae927fbab59bdae810c7b7458 @@ -0,0 +1,364 @@ +I"

标题表示方法

+ +

最多可以使用六级标题

+ +
# 一级标题
+## 二级标题
+### 三级标题
+
+ +

在 vs code中使用md文件

+ +
    +
  1. 如何在 vs code中使用md文件以及查看: 先点击左上角,选择分栏;然后在右边的分栏中,按下快捷键“control+shift+v”预览md文件
  2. +
  3. 或者下载”markdown all in one”插件,方便浏览
  4. +
+ +

表格 | table

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
列1列2列3
行1行1行1
行2行2行2
行3行3行3
+ +
#### 表格 | table
+
+|列1|列2|列3|
+|-|-|-|
+|行1|行1|行1|
+|行2|行2|行2|
+|行3|行3|行3|
+
+ +

冒号在右边表示右对齐

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
列1列2列3
行1行1行1
行2行2行2
行3行3行3
+ +
冒号在右边表示右对齐
+
+|列1|列2|列3|
+|-:|-:|-:|
+|行1|行1|行1|
+|行2|行2|行2|
+|行3|行3|行3|
+
+ +

冒号在左边表示左对齐

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
列1列2列3
行1行1行1
行2行2行2
行3行3行3
+ +
冒号在左边表示左对齐
+
+|列1|列2|列3|
+|:-|:-|:-|
+|行1|行1|行1|
+|行2|行2|行2|
+|行3|行3|行3|
+
+ +

两边都有冒号表示中间对齐

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
列1列2列3
行1行1行1
行2行2行2
行3行3行3
+ +
两边都有冒号表示中间对齐
+
+|列1|列2|列3|
+|:-:|:-:|:-:|
+|行1|行1|行1|
+|行2|行2|行2|
+|行3|行3|行3|
+
+ +

列表

+ +

无序列表

+ + + +
无序列表
+
+* 无序列表
+  * 无序列表2
+    * 无序列表3
+
+ +

有序列表

+ +
    +
  1. 有序列表 +
      +
    1. 有序列表1
    2. +
    +
  2. +
  3. 有序列表 +
      +
    1. 有序列表2
    2. +
    3. 有序列表2
    4. +
    +
  4. +
+ +
有序列表
+
+1. 有序列表
+   1. 有序列表1
+2. 有序列表
+   1. 有序列表2
+   2. 有序列表2
+
+ +

任务列表:

+ +

我明天要做的事情

+ + +
任务列表:
+
+我明天要做的事情
+- [ ] 吃饭
+- [x] 睡觉
+
+ +

注解和脚注

+ +

注解

+
+

这里的内容表示注解

+
+ +
注解
+> 这里的内容表示注解
+
+ +

脚注: +这是一个链接到谷歌的[^脚注]。

+ +
脚注:
+这是一个链接到谷歌的[^脚注]。
+
+[^脚注]: http://www.google.com
+
+ +

图片和超链接

+ +

超链接:

+
    +
  1. 内联的方式: 中括号+小括号 + +
  2. +
  3. 引用的方式:这样就可以使用[文本]+[id]的方式进行引用 + +
  4. +
  5. 还可以链接到标题 + +
  6. +
+ +
超链接:
+1. 内联的方式: 中括号+小括号
+   * This is an [example link](http://www.google.com).
+2. 引用的方式:这样就可以使用[文本]+[id]的方式进行引用
+   * I get 10 times more traffic from [Google][1] than from [Yahoo][2] or [MSN][3].  
+3. 还可以链接到标题
+   * 请参考[标题1](#一级标题)
+
+[1]: http://google.com/        "Google" 
+[2]: http://search.yahoo.com/  "Yahoo Search" 
+[3]: http://search.msn.com/    "MSN Search"
+
+ +

图片格式:比起超链接多了一个感叹号,

+ + +
图片格式:比起超链接多了一个感叹号,
+* 小括号内可以使用URL地址或者本地文件的相对路径
+* 可以空一格之后添加""表示图片的属性,鼠标放上来就可以使用了
+* 和超链接的区别在于多了一个感叹号
+![浙大logo](https://upload.wikimedia.org/wikipedia/zh/thumb/1/16/Zhejiang_University_Logo.svg/1200px-Zhejiang_University_Logo.svg.png "浙大logo"){:height="40%" width="40%"}
+
+ +

嵌入视频

+ +
<iframe 
+src="https://www.bilibili.com/video/BV1D64y1Z7Ko?share_source=copy_web&vd_source=a59f9331c2b80795dda1842d7be401ec" 
+scrolling="no" 
+border="0" 
+frameborder="no" 
+framespacing="0" 
+allowfullscreen="false" 
+height=600 
+width=800>
+</iframe>
+
+ +

文字格式

+ +

这里是斜体

+ +
这里是*斜体*
+
+ +

这里是粗体

+
这里是**粗体**
+
+ +

这里是变量名内容

+
这里是`变量名`内容
+
+ +

下划线:

+ +

要使用HTML中的u标签

+ +

下划线

+ +
下划线:
+
+要使用HTML中的u标签
+
+<u>下划线</u>
+
+ +

==文字高亮==

+ +
==文字高亮==
+
+ +

代码块

+
int main(){
+    return 0;
+}
+
+
+ +

删除线:

+ +

不太行哦

+ +
删除线:
+
+~~不太行哦~~
+
+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/17/c8872dff87747c23df0234bf46a0a8cc4074b6d483a3f0368a568b376c4e95 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/17/c8872dff87747c23df0234bf46a0a8cc4074b6d483a3f0368a568b376c4e95 new file mode 100644 index 0000000000..32f0bce546 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/17/c8872dff87747c23df0234bf46a0a8cc4074b6d483a3f0368a568b376c4e95 @@ -0,0 +1,2 @@ +I"6

D-Tale是什么?

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/18/a586c2bdc8fc8eacd161152e64bf3ebd39c3a0dcc8290a66c7b56d532b04a5 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/18/a586c2bdc8fc8eacd161152e64bf3ebd39c3a0dcc8290a66c7b56d532b04a5 new file mode 100644 index 0000000000..df7e332d50 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/18/a586c2bdc8fc8eacd161152e64bf3ebd39c3a0dcc8290a66c7b56d532b04a5 @@ -0,0 +1,2 @@ +I"3

高效的函数

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/19/6f4b2efdae37069ab0160796712dc995ef2b9bbff94cd172e5a5319c955545 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/19/6f4b2efdae37069ab0160796712dc995ef2b9bbff94cd172e5a5319c955545 new file mode 100644 index 0000000000..de7b8f69a7 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/19/6f4b2efdae37069ab0160796712dc995ef2b9bbff94cd172e5a5319c955545 @@ -0,0 +1,24 @@ +I"

D-Tale是什么?

+ +

Pandas数据框以带有行和列的表格格式表示。它与表格中数据的excel显示非常相似。excel有很多可以操作的界面,比如说数据排序,数据筛选,数据聚合,生成各种图片以及数据透视表等等。Pandas dataframe 和 excel 的显着区别在于,在 Pandas 中,您不能直接对数据集进行修改。您既不能通过任何 GUI 对数据进行排序,也不能通过选择一些参数来创建快速可视化。

+ +

Pandas可以定制化地进行数据探索,但是要记住一些基本的语句,比如说怎么导入导出到csv,怎么进行索引等等,这不太友好;还有的时候,你希望进行一些漫无目的的探索,但是又懒得去看帮助文档或者感觉得到的信息针对性不强。你可能会希望有个GUI可视化界面,操作就和Excel一样简单,并且可以把操作过程记录下来,获得数据探索、数据清洗和绘图的代码,下次面对另外的数据也可以重复该过程。

+ +

这就是D-Tale!!!D-Tale是一个强大的库,它它与jupyter notebooks和python/ipython终端无缝集成,支持诸如DataFrame,Series,MultiIndex,DateTimeIndex和RangeIndex之类的Pandas对象,可以用很轻量的代码量完成日常所需要的数据分析和可视化工作。

+ +

插个楼:Pandas是啥?

+ +

Pandas是啥

+ +

Pandas 是一个开放源码、BSD 许可的库,提供高性能、易于使用的数据结构和数据分析工具。

+ +

Pandas 名字衍生自术语 “panel data”(面板数据)和 “Python data analysis”(Python 数据分析)。

+ +

Pandas 一个强大的分析结构化数据的工具集,基础是 Numpy(提供高性能的矩阵运算)。

+ +

Pandas 可以从各种文件格式比如 CSV、JSON、SQL、Microsoft Excel 导入数据。

+ +

Pandas 可以对各种数据进行运算操作,比如归并、再成形、选择,还有数据清洗和数据加工特征。

+ +

Pandas 广泛应用在学术、金融、统计学等各个数据分析领域。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/1b/94e6e598921e3d1a2c349b244678194fa225e3b4b36d070131b7f7ec2f7da7 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/1b/94e6e598921e3d1a2c349b244678194fa225e3b4b36d070131b7f7ec2f7da7 new file mode 100644 index 0000000000..070faae1dc --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/1b/94e6e598921e3d1a2c349b244678194fa225e3b4b36d070131b7f7ec2f7da7 @@ -0,0 +1,18 @@ +I"B + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/1f/b37e746e066360c85160b0354c48ce6b6e85d874cf6e2fe08633ecf44a4bba b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/1f/b37e746e066360c85160b0354c48ce6b6e85d874cf6e2fe08633ecf44a4bba new file mode 100644 index 0000000000..dc9b60fac4 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/1f/b37e746e066360c85160b0354c48ce6b6e85d874cf6e2fe08633ecf44a4bba @@ -0,0 +1,92 @@ +I"

这一讲主要是介绍了set(集合)数据结构,三元运算符等等内容。

+ +

set(集合)数据结构

+ +

set(集合)是一个非常有用的数据结构。它与列表(list)的行为类似,区别在于set不能包含重复的值。 +这在很多情况下非常有用。例如你可能想检查列表中是否包含重复的元素,你有两个选择,第一个需要使用for循环,就像这样:

+ +
some_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n']
+
+duplicates = []
+for value in some_list:
+    if some_list.count(value) > 1:
+        if value not in duplicates:
+            duplicates.append(value)
+
+print(duplicates)
+
+ +

输出结果为:

+ +
['b', 'n']
+
+ +

但还有一种更简单更优雅的解决方案,那就是使用集合(sets),你直接这样做:

+
some_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n']
+duplicates = set([x for x in some_list if some_list.count(x) > 1])
+print(duplicates)
+
+ +

交集

+

你可以对比两个集合的交集(两个集合中都有的数据),如下:

+
valid = set(['yellow', 'red', 'blue', 'green', 'black'])
+input_set = set(['red', 'brown'])
+print(input_set.intersection(valid))
+
+

输出结果为:{‘red’}

+ +

差集

+ +

你可以用差集(difference)找出无效的数据,相当于用一个集合减去另一个集合的数据,例如:

+ +
valid = set(['yellow', 'red', 'blue', 'green', 'black'])
+input_set = set(['red', 'brown']) 
+print(input_set.difference(valid)) 
+
+
输出结果为:{'brown'}
+
+### 并集
+你可以用并集(union)来合并两个集合,如下:
+
+```python
+valid = set(['yellow', 'red', 'blue', 'green', 'black'])
+input_set = set(['red', 'brown'])
+print(input_set.union(valid))
+
+

输出结果为:{‘brown’, ‘red’, ‘green’, ‘black’, ‘blue’, ‘yellow’}

+ +

三元运算符

+ +

三元运算符通常在Python里被称为条件表达式,这些表达式基于真(true)/假(not)的条件判断,在Python 2.4以上才有了三元操作。

+ +

它允许用简单的一行快速判断,而不是使用复杂的多行if语句。 这在大多数时候非常有用,而且可以使代码简单可维护。

+ +

伪代码如下:

+ +
condition_is_true if condition else condition_is_false
+
+

举例子:

+
is_fat = True
+state = "fat" if is_fat else "not fat"
+print(state)
+
+ +

输出结果为:fat

+ +

另一个晦涩一点的用法比较少见,它使用了元组,请继续看:

+ +

伪代码:

+
#(返回假,返回真)[真或假]
+(if_test_is_false, if_test_is_true)[test]
+
+
fat = True
+fitness = ("skinny", "fat")[fat]
+print("Ali is ", fitness)
+
+

输出结果为:Ali is fat

+ +

上面的例子没有被广泛使用,而且Python玩家一般不喜欢那样,因为没有Python味儿(Pythonic)。这样的用法很容易把真正的数据与true/false弄混。

+ +

另外一个不使用元组条件表达式的缘故是因为在元组中会把两个条件都执行,而 if-else 的条件表达式不会这样。

+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/22/ac9970185e7c70150e59c80d98a6f72516ef7dbd2057c97deaa9eb2bd743c1 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/22/ac9970185e7c70150e59c80d98a6f72516ef7dbd2057c97deaa9eb2bd743c1 new file mode 100644 index 0000000000..a426ba9af0 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/22/ac9970185e7c70150e59c80d98a6f72516ef7dbd2057c97deaa9eb2bd743c1 @@ -0,0 +1,47 @@ +I"

安装Cockpit

+ +
sudo apt install cockpit -y
+
+ +

-y参数表示自动安装,不需要手动输入y。

+ +

或者使用如下命令进行安装,/etc/os-release表示系统版本信息,${VERSION_CODENAME}表示系统版本代号,如Ubuntu 18.04的版本代号为bionic。

+ +
. /etc/os-release
+sudo apt install -t ${VERSION_CODENAME}-backports cockpit
+
+ +

启动Cockpit

+ +
sudo systemctl start cockpit
+#sudo systemctl enable cockpit
+
+
+ +

查看状态

+ +
apt -qq list cockpit # 查看版本
+sudo systemctl status cockpit
+
+ +

设置防火墙

+ +
ss -tunlp | grep 9090
+
+ +

ss 表示查看socket状态,-t表示TCP协议,-u表示UDP协议,-n表示不解析IP地址,-l表示监听状态,-p表示显示进程信息。grep 9090表示过滤出端口为9090的进程。如果没有显示9090端口,则表示9090端口没有被占用,可以直接使用。如果显示了9090端口,则需要修改端口号。

+ +
sudo ufw allow 9090/tcp
+
+ +

/tcp表示TCP协议,/udp表示UDP协议。

+ +

访问Cockpit

+ +

使用ifconfig命令查看服务器IP地址,然后在浏览器中输入服务器IP地址加端口9090,如http://IP_OF_SERVER:9090。

+ +

一旦你登录后,你可以看到 Cockpit 的主界面,在这里可以直观的看到服务器的运行状态,包括CPU使用率、内存使用率、硬盘使用率等。

+ +

在cockpit中我们可以很方便的管理各系统服务,包括磁盘管理,网络管理,系统服务(包括定时任务管理),浏览和搜索系统日志,升级软件包到最新版本等等。

+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/23/4a0b22f5b23be85456d0cb3e9b3c647e50fa8821d78da21a9af626da5ce5a3 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/23/4a0b22f5b23be85456d0cb3e9b3c647e50fa8821d78da21a9af626da5ce5a3 new file mode 100644 index 0000000000..98ec701f46 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/23/4a0b22f5b23be85456d0cb3e9b3c647e50fa8821d78da21a9af626da5ce5a3 @@ -0,0 +1,2 @@ +I"_

Markdowm插件Office Viewer介绍以及vscode+PicGO+Github搭建个人博客图床

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/25/2c611e1d1a45db5ef218a25c298e340f1a20ed9f7d061176e4c212a1cb4fd8 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/25/2c611e1d1a45db5ef218a25c298e340f1a20ed9f7d061176e4c212a1cb4fd8 new file mode 100644 index 0000000000..18bd8f7344 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/25/2c611e1d1a45db5ef218a25c298e340f1a20ed9f7d061176e4c212a1cb4fd8 @@ -0,0 +1,2 @@ +I"

介绍了接口自动化测试框架框架搭建,第三方插件,配置文件设置和读取,测试流程等内容。测试用例设计部分,则包括Excel测试用例,控制流和数据流,方法的应用场景,测试用例书写标准以及测试集的创建。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/25/6fe6701d50bca94aaf79e752af7a78c47890763e85b1d06e17cd628f080af6 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/25/6fe6701d50bca94aaf79e752af7a78c47890763e85b1d06e17cd628f080af6 new file mode 100644 index 0000000000..98d576fdd4 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/25/6fe6701d50bca94aaf79e752af7a78c47890763e85b1d06e17cd628f080af6 @@ -0,0 +1,2 @@ +I"9

日志文件备份

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/29/3a89eb91f0bc79e86037ac9f8678065b6835f0c0a60c15b4a5a02ef84aba10 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/29/3a89eb91f0bc79e86037ac9f8678065b6835f0c0a60c15b4a5a02ef84aba10 new file mode 100644 index 0000000000..ae26de4f09 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/29/3a89eb91f0bc79e86037ac9f8678065b6835f0c0a60c15b4a5a02ef84aba10 @@ -0,0 +1,51 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/2a/4e3cd8a410691557d824241f92fbce86f9802e403e2c7ccd86b1eaaf928123 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/2a/4e3cd8a410691557d824241f92fbce86f9802e403e2c7ccd86b1eaaf928123 new file mode 100644 index 0000000000..e0e3c50f31 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/2a/4e3cd8a410691557d824241f92fbce86f9802e403e2c7ccd86b1eaaf928123 @@ -0,0 +1,89 @@ +I"

JMeter安装

+ +

安装Java

+ +

JMeter是基于Java开发的,所以需要先安装Java环境,包括JDK,JAVA开发工具包和JRE,Java运行环境。JDK是Java开发工具包,包含了Java的开发工具,如:Java编译器、Java文档生成器、Java调试器等。JDK是Java开发的基础,JRE是Java运行的基础,JVM是Java运行的平台。

+ +

下载地址:Java 19 and Java 17 available now +

+ +

下载选择Linux x64版本(x64 Compressed Archive),下载完成后,解压到指定目录,如:/usr/local

+ +
sudo tar -zxvf jdk-19_linux-x64_bin.tar.gz -C /usr/local # -C 指定解压目录
+
+ +

修改环境变量

+ +
sudo vim /etc/profile
+
+ +

在/etc/profile文件末尾添加以下内容:

+ +
JAVA_HOME=/usr/local/jdk-19.0.2 # JDK安装目录
+CLASSPATH=.:$JAVA_HOME/lib.tools.jar # 这个变量设置的目的是为了程序能找到相应的被执行的".class"文件
+PATH=$PATH:$JAVA_HOME/bin # BIN是JDK的可执行文件目录
+export JAVA_HOME CLASSPATH PATH
+
+ +

重新加载/etc/profile配置文件

+ +
source /etc/profile
+
+ +

验证Java安装

+ +
java -version #
+
+ +
javac -version 
+
+ +

安装JMeter

+ +

Apache JMeter 5.5安装地址,选择Binaries中的apache-jmeter-5.5.tgz下载。

+ +

如果觉得速度太慢,可以切换到国内的阿里云镜像下载

+ +

解压缩

+ +
tar -zxvf apache-jmeter-5.5.tgz
+
+ +

配置JMeter环境变量

+ +
sudo vim /etc/profile
+
+ +

在/etc/profile文件末尾添加以下内容:

+ +
export JMETER_HOME=/usr/local/apache-jmeter-5.5 # JMeter安装目录
+export PATH=$JMETER_HOME/bin:$PATH
+
+ +

重新加载/etc/profile配置文件

+ +
source /etc/profile
+
+ +

验证JMeter安装

+ +
jmeter -v
+
+ +

运行JMeter

+ +
jmeter
+
+ +

JMeter入门

+ +

设置

+ +

修改语言为中文,如下图所示,打开options,选择choose Language,选择Chinese(simplified) +修改语言为中文

+ +

防止中文乱码,打开jmeter.properties文件,(位于apache-jmeter-5.5/bin目录下),修改以下内容:

+ +
sampleresult.default.encoding=UTF-8
+
+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/2a/6cd039fa285927e4f821854dac2a9de62ab652d31a215013167770b17d2be5 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/2a/6cd039fa285927e4f821854dac2a9de62ab652d31a215013167770b17d2be5 new file mode 100644 index 0000000000..434cf5d0fd --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/2a/6cd039fa285927e4f821854dac2a9de62ab652d31a215013167770b17d2be5 @@ -0,0 +1,10 @@ +I"} + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/2f/26f8a53bee031b5bca22829bbe634b92071dbb0430145658807f1c65689eb5 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/2f/26f8a53bee031b5bca22829bbe634b92071dbb0430145658807f1c65689eb5 new file mode 100644 index 0000000000..6447d78acc --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/2f/26f8a53bee031b5bca22829bbe634b92071dbb0430145658807f1c65689eb5 @@ -0,0 +1,2 @@ +I"-

数据介绍

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/30/edda9a59a60dbeb58dbeacd27a4ccb695dc65e7d4ed65bcbe099e19915cbda b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/30/edda9a59a60dbeb58dbeacd27a4ccb695dc65e7d4ed65bcbe099e19915cbda new file mode 100644 index 0000000000..069881f7ed --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/30/edda9a59a60dbeb58dbeacd27a4ccb695dc65e7d4ed65bcbe099e19915cbda @@ -0,0 +1,2 @@ +I"O

1. 什么是stable diffusion?

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/31/e708d316d6e683973ed3c1992c16427aa1992c8b232d818f4837e6f856f2db b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/31/e708d316d6e683973ed3c1992c16427aa1992c8b232d818f4837e6f856f2db new file mode 100644 index 0000000000..fc9c819c36 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/31/e708d316d6e683973ed3c1992c16427aa1992c8b232d818f4837e6f856f2db @@ -0,0 +1,582 @@ +I"o3

美多商城接口自动化测试项目框架代码

+ +

utils

+ +

AssertUtil.py

+ + + +

验证返回状态码

+ +

assert int(code) == int(expected_code)

+ +
- assert_body方法
+
+ +

验证返回结果内容相等

+ +

assert body == expected_body

+ +
- assert_in_body方法
+
+ +

验证返回结果是否包含期望的结果

+ +

assert expected_body in body

+ +

EmailUtil.py

+ + + +

ExcelUtil.py

+ + + +

LogUtil.py

+ + + +

MysqlUtil.py

+ +

大多数情况下,我们接口自动化测试需要对数据库进行验证,比如说,我们在注册接口中,需要验证数据库中是否有新增的数据,这时候我们就需要对数据库进行查询,然后对查询结果进行断言。比如说,我们需要对订单接口进行测试,需要验证数据库中的订单数据是否正确,这时候我们就需要对数据库进行查询,然后对查询结果进行断言。

+ + + +

RequestsUtil.py

+ + + +

YamlUtil.py

+ + + +

config

+ +

Conf.py

+ + + +

conf.yml

+ + + +

db_conf.yml

+ + + +

common

+ +

Base.py

+ + + +

ExcelConfig.py

+ + + +

ExcelData.py

+ + + +

data

+ +

testdata.xlsx

+ + + +

testlogin.yml

+ + + +

logs

+ +

日志文件名称

+ +

日志文件名称= logs目录 + 当前时间+扩展名

+ +

日志文件内容

+ +

’%(asctime)s %(name)s %(levelname)s %(message)s ‘

+ +

report

+ +

report.html

+ +

result

+ + + +

run and requirement

+ +

pytest.ini

+ + + +

requirements.txt

+ + + +

run.py执行测试用例

+ + + +

testcase

+ +

t_log

+ + + +

t_allure

+ + + +

t_pytest

+ + + +

t_excel

+ + + +

t_yml

+ + + +

t_subprocess

+ + + +

test_login.py

+ + + +

test_excel_case.py

+ + + +

test_Mall.py

+ + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/32/049041389b13152cbd760c574c739070b5095098efea4509531255073d54d5 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/32/049041389b13152cbd760c574c739070b5095098efea4509531255073d54d5 new file mode 100644 index 0000000000..0c4842e3d4 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/32/049041389b13152cbd760c574c739070b5095098efea4509531255073d54d5 @@ -0,0 +1,2 @@ +I"-

数据来源

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/36/232033e9857c977c92195e0d6c9a80efaf64381291304d9c0e66b0b9d3791a b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/36/232033e9857c977c92195e0d6c9a80efaf64381291304d9c0e66b0b9d3791a new file mode 100644 index 0000000000..c357b628fc --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/36/232033e9857c977c92195e0d6c9a80efaf64381291304d9c0e66b0b9d3791a @@ -0,0 +1,157 @@ +I"o

1. 随机生成一个随机的string

+ +

20230403105015

+ +

20230403105045

+ +

结果如下:(根据和github里面搜出来的代码块基本上是一样的,挺简洁的)

+ +

20230403105129

+ +

调整一下就可以直接使用

+ +
import random
+import string
+
+# generate a string of random characters
+def generate_random_string(length):
+    letters = string.ascii_lowercase
+    result_str = ''.join(random.choice(letters) for i in range(length))
+    return result_str
+
+print(generate_random_string(10))
+
+ +

运行代码,结果如下:

+ +

20230403105320

+ +

2. 对字符串进行切片

+ +

首先,我大概想了下以后可能要处理的文件的文件名,可能是这个格式,最开始是设备的安装日期,然后是地点,机组号码,传感器编号,最后是这个装置的布防期间,大概是这样滴。

+ +

string1= '2015-01-01-内蒙-机组304-308传感器-测试期间-from-2016-03-04-to-2018-05-07'

+ +

接着,我希望codecursor帮我写代码切割字符串:

+ +

20230403105632

+ +

得到的建议代码如下:

+ +

20230403105727

+ +

print一下结果发现是可以直接用的,已经切割出了符合我们预期的substring:

+ +

20230403105839

+ +
string1= '2015-01-01-内蒙-机组304-308传感器-测试期间-from-2016-03-04-to-2018-05-07'
+
+
+year = string1[0:4]
+month = string1[5:7]
+day = string1[8:10]
+region = string1[11:13]
+unit = string1[14:19]
+sensor = string1[20:26]
+start_date = string1[37:47]
+end_date = string1[51:61]
+print(year, month, day, region, unit, sensor,start_date, end_date)
+
+ +

但是我试了下,在大部分情况下,他提取的substring都有问题,最好是print出来看看,然后再调整一下。

+ +

3. 批量提取字符串中的信息

+ +

我们距离目标很近了,有时候我们期待我们可以对多个文件进行操作,那么这时候我们可能需要构造一个提取信息的函数帮助我们完成这个任务。

+ +

首先,我们生成一系列和string1格式相同的字符串,看看codecursor能不能帮我们做到这一点:

+ +

20230403110300

+ +

20230403110418

+ +

神了,它居然这都可以做到。

+ +

然后,我们构造提取信息的函数,在这里就不演示codecursor的操作了,直接上它帮助我们写好的代码:

+ +
# 生成新的字符串
+
+string3 = '2022-01-01-北京-机组101-305传感器-测试期间-from-2023-03-04-to-2025-05-07'
+string4 = '2023-02-01-上海-机组102-306传感器-测试期间-from-2024-03-04-to-2026-05-07'
+string5 = '2024-03-01-广州-机组103-307传感器-测试期间-from-2025-03-04-to-2027-05-07'
+string6 = '2025-04-01-深圳-机组104-308传感器-测试期间-from-2026-03-04-to-2028-05-07'
+string7 = '2026-05-01-成都-机组105-309传感器-测试期间-from-2027-03-04-to-2029-05-07'
+string8 = '2027-06-01-重庆-机组106-310传感器-测试期间-from-2028-03-04-to-2030-05-07'
+
+# 构造提取信息的函数
+def extract_info(string):
+    year = string[0:4]
+    month = string[5:7]
+    day = string[8:10]
+    region = string[11:13]
+    unit = string[14:19]
+    sensor = string[20:26]
+    start_date = string[37:47]
+    end_date = string[51:61]
+    return year, month, day, region, unit, sensor, start_date, end_date
+year, month, day, region, unit, sensor, start_date, end_date = extract_info(string3)
+print(year, month, day, region, unit, sensor, start_date, end_date)
+
+ +

运行结果如下:

+ +

20230403110647

+ +

4.批量保存提取的信息

+ +

还是跳过操作步骤,给出codecursor给的代码:

+ +
# 将列表转换为表格
+string_list = [string1,string3, string4, string5, string6, string7, string8]
+table = []
+for string in string_list:
+    info = extract_info(string)
+    table.append(info)
+print(table)
+
+# 将表格转换为DataFrame,并保存为csv文件
+import pandas as pd
+df = pd.DataFrame(table, columns=['year', 'month', 'day', 'region', 'unit', 'sensor', 'start_date', 'end_date'])
+df.to_csv('string.csv', index=False)
+
+ +

运行代码,结果如下:

+ +

20230403110911

+ +

5.其他探索

+ +

生成新的字符串那一步我不太满意,这次我希望他可以帮助我自动生成和string1一样格式的字符串,它给出的代码是这样的:

+ +
import random
+def generate_random_string():
+    year = str(random.randint(2022, 2027))
+    month = str(random.randint(1, 12)).zfill(2)
+    day = str(random.randint(1, 28)).zfill(2)
+    region = random.choice(['北京', '上海', '广州', '深圳', '成都', '重庆'])
+    unit = '机组' + str(random.randint(101, 106))
+    sensor = str(random.randint(305, 310)) + '传感器'
+    start_year = str(random.randint(int(year)+1, 2029)) # int()方法用于将字符串转换为整数, int(year)+1表示将字符串转换为整数后加1
+    start_month = str(random.randint(1, 12)).zfill(2) # zfill()方法用于将字符串的长度补全为指定的长度,不足的部分用0填充
+    start_day = str(random.randint(1, 28)).zfill(2)
+    end_year = str(random.randint(int(start_year)+1, 2030))
+    end_month = str(random.randint(1, 12)).zfill(2)
+    end_day = str(random.randint(1, 28)).zfill(2)
+    string = year + '-' + month + '-' + day + '-' + region + '-' + unit + '-' + sensor + '-测试期间-from-' + start_year + '-' + start_month + '-' + start_day + '-to-' + end_year + '-' + end_month + '-' + end_day
+    return string
+
+string_list = []
+for i in range(20):
+ string_list.append(generate_random_string())
+print(string_list)
+
+ +

运行结果如下:

+ +

20230403111413

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/37/11d8d510f61dc36eb80ed8da0f9415fa9053eaa4a2a186cffc2e76a1c4e6d6 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/37/11d8d510f61dc36eb80ed8da0f9415fa9053eaa4a2a186cffc2e76a1c4e6d6 new file mode 100644 index 0000000000..513e74f493 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/37/11d8d510f61dc36eb80ed8da0f9415fa9053eaa4a2a186cffc2e76a1c4e6d6 @@ -0,0 +1,62 @@ +I",

字符串是指由零个或多个字符组成的有限序列,是编程语言中最常用的数据类型之一。字符串是由单引号或双引号括起来的任意文本,比如’abc’,”xyz”等等。请注意,’‘或”“本身只是一种表示方式,不是字符串的一部分,因此,字符串’abc’只有a,b,c这3个字符。

+ +

字符串的格式化是指在字符串中插入变量,格式化字符串的输出方式有很多种,比如通过%操作符,format方法,字符串字面值插值等。

+ +

通过%操作符格式化

+ +
'Hell %s' % 'world'
+
+ +

%s 表示字符串,%d 表示整数,%f 表示浮点数,%x 表示十六进制整数。

+ +
# 多个%操作符
+'we have bought %d apples, %d oranges and %d pears' % (3, 5, 2)
+
+ +
'we have %d apples, and the apples come from %s' % (3, 'China')
+
+ +

通过format方法格式化

+ +
'Hello {0},welcome to {1}.'.format('Jack','China') # 0表示第一个参数,1表示第二个参数
+
+ +

你还可以用别名以任意顺序替换变量。这是一个非常强大的功能,不必修改传递给格式函数的参数就可以重新排列显示顺序。

+ +
'Hey {name}, welcome to {country}.'.format(country='China',name='Jack')
+
+ +

字符串字面值插值

+ +

Python 3.6 增加了另一种格式化字符串的方法,称为格式化字符串字面值(formatted string literal)。采用这种方法,可以在字符串常量内使用嵌入的 Python 表达式。

+ +
a = 3
+b = 5
+f'we have bought {a} apples, {b} oranges and now the total fruit number is {a+b}.'
+
+ +

本质上,格式化字符串字面值是 Python 解析器的功能:将 f 字符串转换为一系列字符串常量和表达式,然后合并起来构建最终的字符串。

+ +

这种方法的优点是,它不仅更简洁,而且还更快。因为它不需要调用任何函数,而是由解析器在编译时处理。

+ +

在函数中使用该格式化语法,如下:

+ +
def greet(name, question):
+    return f"Hello, {name}! How's it {question}?"
+greeter = greet('Jack', 'going')
+print(greeter)
+
+ +

模板字符串

+ +

其他字符串格式化技术所用的语法更复杂,因而可能会给程序带来安全漏洞。因此,Python 3.6 引入了一种新的字符串格式化方法,称为模板字符串(template string)。

+ +
from string import Template
+t = Template('we have bought $num1 apples, $num2 oranges and now the total fruit number is $total.')
+t.substitute(num1=3,num2=5,total=8)
+
+ +

来源

+ +

《深入理解Python特性》P022 - P027

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/37/d4f75def712769cc7babb59a4ff3fb576c533d02781cdbdd8fa964a25391fa b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/37/d4f75def712769cc7babb59a4ff3fb576c533d02781cdbdd8fa964a25391fa new file mode 100644 index 0000000000..04622f4608 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/37/d4f75def712769cc7babb59a4ff3fb576c533d02781cdbdd8fa964a25391fa @@ -0,0 +1,476 @@ +I"Z|

Docker相关的概念

+ +

1.1 虚拟机和容器

+ +

借助于VMWare等软件,可以在一台计算机上创建多个虚拟机,每个虚拟机都拥有独立的操作系统,可以各自独立的运行程序。这种分身术虽然隔离度高(操作系统级),使用方便(类似物理机),但占用存储资源多(GB级)、启动速度慢(分钟级)的缺点也是显而易见的。

+ +

相较于虚拟机,容器(Container)是一种轻量型的虚拟化技术,它虚拟的是最简运行环境(类似于沙盒)而非操作系统,启动速度快(秒级)、占用存储资源少(KB级或MB级),容器间隔离度为进程级。在一台计算机上可以运行上千个容器,这是容器技术对虚拟机的碾压式优势。

+ +

1.2 容器、镜像和Docker

+ +

Docker是一个开源的应用容器引擎,可以创建容器以及基于容器运行的程序。Docker可以让开发者打包他们的应用和依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。在Docker和容器之间,还隐藏着一个镜像的概念。本质上,Docker镜像是一个特殊的文件系统,它提供容器运行时所需的程序、库、资源、配置等文件。Docker镜像类似于一个py文件,它需要Docker的运行时(类似于Python解释器)运行。镜像被运行时,即创建了一个镜像的实例,一个实例就是一个容器。

+ +

镜像 +是一个构建容器的只读模板,是一个包含了应用程序和其运行时依赖环境的只读文件。它包含了容器启动所需的所有信息,包括运行程序和配置数据。每一个镜像由一系列的层 (layers) 组成。Docker 使用 UnionFS 来将这些层联合到单独的镜像中。UnionFS 允许独立文件系统中的文件和文件夹(称之为分支)被透明覆盖,形成一个单独连贯的文件系统。正因为有了这些层的存在,Docker 是如此的轻量。当你改变了一个 Docker 镜像,比如升级到某个程序到新的版本,一个新的层会被创建。因此,不用替换整个原先的镜像或者重新建立(在使用虚拟机的时候你可能会这么做),只是一个新的层被添加或升级了。现在你不用重新发布整个镜像,只需要升级,层使得分发 Docker 镜像变得简单和快速。

+ +

容器 +容器是从镜像创建的运行实例。它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。Docker 利用容器来运行应用。一个 Docker 容器都是独立和安全的应用平台,Docker 容器是 Docker 的运行部分。可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。

+ +

注: 镜像是只读的,容器在启动的时候创建一层可写层作为最上层。

+ +

20230329151449

+ +

运行容器实例有两种方式:

+ +

交互型容器: 前台运行,可以通过控制台与容器交互。如果创建该容器的终端被关闭,则容器就变为停止状态。此外,在容器控制台中输入exit或者通过docker stop或docker kill也能终止容器。 +后台型容器: 后台运行,创建启动之后就与终端无关了,需要用 docker stop 或 docker kill 来终止,或者 docker logs 查看运行中的输出。 +仓库 +Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。同样的,Docker 仓库也有公有和私有的概念。公有的 Docker 仓库名字是 Docker Hub。Docker Hub 提供了庞大的镜像集合供使用。这些镜像可以是自己创建,或者在别人的镜像基础上创建。Docker 仓库是 Docker 的分发部分。

+ +

Docker 仓库的概念与 Git 版本控制工具有些类似,注册服务器可以理解为 GitHub 这样的托管服务。

+ +

Docker的安装

+ +

2.1 在ubuntu中安装

+ +

在linux系统中安装Docker非常简单,官方为我们提供了一键安装脚本。这个方法也适用于Debian或CentOS等发行版。

+ +
curl -sSL https://get.daocloud.io/docker | sh
+
+ +

安装过程如果出现超时,不要灰心,多试几次,总会成功的。安装完成后,Docker只能被root用户使用,可以使用下面的命令取消权限限制:

+ +
sudo gpasswd -a <你的用户名> docker
+
+ +

然后,重启docker服务:

+ +
sudo service docker restart
+
+ +

最后,关闭当前的命令行,重新打开新的命令行就可以了。

+ +

Docker镜像的使用

+ +

docker官方的镜像库比较慢,在进行镜像操作之前,需要将镜像源设置为国内的站点。

+ +

新建文件/etc/docker/daemon.json,输入如下内容:

+ +
{
+    "registry-mirrors" : [
+        "https://registry.docker-cn.com",
+        "https://docker.mirrors.ustc.edu.cn",
+        "http://hub-mirror.c.163.com",
+        "https://cr.console.aliyun.com/"
+    ]
+}
+
+ +

然后重启docker的服务:

+ +
systemctl restart docker
+
+ +

3.1 列出本地所有镜像

+ +

执行命令 docker images 可以查看

+ +
$ docker images
+REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
+ubuntu              20.04               f643c72bc252        5 weeks ago         72.9MB
+hello-world         latest              bf756fb1ae65        12 months ago       13.3kB
+
+ +

当前我本地只有刚才安装的两个镜像。

+ +

3.2 从镜像库中查找镜像

+ +

执行命令 docker search 镜像名称可以从docker镜像库中查找镜像。

+ +
$ docker search python
+NAME                             DESCRIPTION                                     STARS              OFFICIAL          AUTOMATED
+python                           Python is an interpreted, interactive, objec…   5757                [OK]                
+django                           Django is a free web application framework, …   1039                [OK]                
+pypy                             PyPy is a fast, compliant alternative implem…   260                 [OK]                
+joyzoursky/python-chromedriver   Python with Chromedriver, for running automa…   57                                      [OK]
+nikolaik/python-nodejs           Python with Node.js                             57                                      [OK]
+arm32v7/python                   Python is an interpreted, interactive, objec…   53                                      
+circleci/python                  Python is an interpreted, interactive, objec…   42                                      
+centos/python-35-centos7         Platform for building and running Python 3.5…   38                                      
+centos/python-36-centos7         Platform for building and running Python 3.6…   30                                      
+hylang                           Hy is a Lisp dialect that translates express…   29                  [OK]                
+arm64v8/python                   Python is an interpreted, interactive, objec…   24                                      
+revolutionsystems/python         Optimized Python Images                         18                                      
+centos/python-27-centos7         Platform for building and running Python 2.7…   17                                      
+bitnami/python                   Bitnami Python Docker Image                     10                                      [OK]
+publicisworldwide/python-conda   Basic Python environments with Conda.           6                                       [OK]
+d3fk/python_in_bottle            Simple python:alpine completed by Bottle+Req…   5                                       [OK]
+dockershelf/python               Repository for docker images of Python. Test…   5                                       [OK]
+clearlinux/python                Python programming interpreted language with…   4                                       
+i386/python                      Python is an interpreted, interactive, objec…   3                                       
+ppc64le/python                   Python is an interpreted, interactive, objec…   2                                       
+centos/python-34-centos7         Platform for building and running Python 3.4…   2                                       
+amd64/python                     Python is an interpreted, interactive, objec…   1                                       
+ccitest/python                   CircleCI test images for Python                 0                                       [OK]
+s390x/python                     Python is an interpreted, interactive, objec…   0                                       
+saagie/python                    Repo for python jobs                            0
+
+ +

最好选择官方(OFFICIAL)的镜像,这样的镜像最稳定一些。

+ +

3.3 下载新的镜像

+ +

执行命令docker pull 镜像名称:版本号即可下载新的镜像。

+ +
$ docker pull python:3.8
+3.8: Pulling from library/python
+6c33745f49b4: Pull complete 
+ef072fc32a84: Pull complete 
+c0afb8e68e0b: Pull complete 
+d599c07d28e6: Pull complete 
+f2ecc74db11a: Pull complete 
+26856d31ce86: Pull complete 
+2cd68d824f12: Pull complete 
+7ea1535f18c3: Pull complete 
+2bef93d9a76e: Pull complete 
+Digest: sha256:9079aa8582543494225d2b3a28fce526d9a6b06eb06ce2bac3eeee592fcfc49e
+Status: Downloaded newer image for python:3.8
+docker.io/library/python:3.8
+
+ +

镜像下载后,就可以使用镜像来创建容器了。

+ +

Docker容器的使用

+ +

4.1 启动容器

+ +

执行命令docker run即可启动容器,也就是创建某个镜像的实例。docker run命令非常复杂,可以先执行一个docker run –help来查看帮助:

+ +
$ docker run --help
+
+Usage:  docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
+
+Run a command in a new container
+
+Options:
+      --add-host list                  Add a custom host-to-IP mapping (host:ip)
+  -a, --attach list                    Attach to STDIN, STDOUT or STDERR
+      --blkio-weight uint16            Block IO (relative weight), between 10 and 1000, or 0 to disable (default 0)
+      --blkio-weight-device list       Block IO weight (relative device weight) (default [])
+      --cap-add list                   Add Linux capabilities
+      --cap-drop list                  Drop Linux capabilities
+      --cgroup-parent string           Optional parent cgroup for the container
+      --cidfile string                 Write the container ID to the file
+      --cpu-period int                 Limit CPU CFS (Completely Fair Scheduler) period
+      --cpu-quota int                  Limit CPU CFS (Completely Fair Scheduler) quota
+      --cpu-rt-period int              Limit CPU real-time period in microseconds
+      --cpu-rt-runtime int             Limit CPU real-time runtime in microseconds
+  -c, --cpu-shares int                 CPU shares (relative weight)
+      --cpus decimal                   Number of CPUs
+      --cpuset-cpus string             CPUs in which to allow execution (0-3, 0,1)
+      --cpuset-mems string             MEMs in which to allow execution (0-3, 0,1)
+  -d, --detach                         Run container in background and print container ID
+      --detach-keys string             Override the key sequence for detaching a container
+      --device list                    Add a host device to the container
+      --device-cgroup-rule list        Add a rule to the cgroup allowed devices list
+      --device-read-bps list           Limit read rate (bytes per second) from a device (default [])
+      --device-read-iops list          Limit read rate (IO per second) from a device (default [])
+      --device-write-bps list          Limit write rate (bytes per second) to a device (default [])
+      --device-write-iops list         Limit write rate (IO per second) to a device (default [])
+      --disable-content-trust          Skip image verification (default true)
+      --dns list                       Set custom DNS servers
+      --dns-option list                Set DNS options
+      --dns-search list                Set custom DNS search domains
+      --domainname string              Container NIS domain name
+      --entrypoint string              Overwrite the default ENTRYPOINT of the image
+  -e, --env list                       Set environment variables
+      --env-file list                  Read in a file of environment variables
+      --expose list                    Expose a port or a range of ports
+      --gpus gpu-request               GPU devices to add to the container ('all' to pass all GPUs)
+      --group-add list                 Add additional groups to join
+      --health-cmd string              Command to run to check health
+      --health-interval duration       Time between running the check (ms|s|m|h) (default 0s)
+      --health-retries int             Consecutive failures needed to report unhealthy
+      --health-start-period duration   Start period for the container to initialize before starting health-retries countdown (ms|s|m|h) (default 0s)
+      --health-timeout duration        Maximum time to allow one check to run (ms|s|m|h) (default 0s)
+      --help                           Print usage
+  -h, --hostname string                Container host name
+      --init                           Run an init inside the container that forwards signals and reaps processes
+  -i, --interactive                    Keep STDIN open even if not attached
+      --ip string                      IPv4 address (e.g., 172.30.100.104)
+      --ip6 string                     IPv6 address (e.g., 2001:db8::33)
+      --ipc string                     IPC mode to use
+      --isolation string               Container isolation technology
+      --kernel-memory bytes            Kernel memory limit
+  -l, --label list                     Set meta data on a container
+      --label-file list                Read in a line delimited file of labels
+      --link list                      Add link to another container
+      --link-local-ip list             Container IPv4/IPv6 link-local addresses
+      --log-driver string              Logging driver for the container
+      --log-opt list                   Log driver options
+      --mac-address string             Container MAC address (e.g., 92:d0:c6:0a:29:33)
+  -m, --memory bytes                   Memory limit
+      --memory-reservation bytes       Memory soft limit
+      --memory-swap bytes              Swap limit equal to memory plus swap: '-1' to enable unlimited swap
+      --memory-swappiness int          Tune container memory swappiness (0 to 100) (default -1)
+      --mount mount                    Attach a filesystem mount to the container
+      --name string                    Assign a name to the container
+      --network network                Connect a container to a network
+      --network-alias list             Add network-scoped alias for the container
+      --no-healthcheck                 Disable any container-specified HEALTHCHECK
+      --oom-kill-disable               Disable OOM Killer
+      --oom-score-adj int              Tune host's OOM preferences (-1000 to 1000)
+      --pid string                     PID namespace to use
+      --pids-limit int                 Tune container pids limit (set -1 for unlimited)
+      --platform string                Set platform if server is multi-platform capable
+      --privileged                     Give extended privileges to this container
+  -p, --publish list                   Publish a container's port(s) to the host
+  -P, --publish-all                    Publish all exposed ports to random ports
+      --read-only                      Mount the container's root filesystem as read only
+      --restart string                 Restart policy to apply when a container exits (default "no")
+      --rm                             Automatically remove the container when it exits
+      --runtime string                 Runtime to use for this container
+      --security-opt list              Security Options
+      --shm-size bytes                 Size of /dev/shm
+      --sig-proxy                      Proxy received signals to the process (default true)
+      --stop-signal string             Signal to stop a container (default "SIGTERM")
+      --stop-timeout int               Timeout (in seconds) to stop a container
+      --storage-opt list               Storage driver options for the container
+      --sysctl map                     Sysctl options (default map[])
+      --tmpfs list                     Mount a tmpfs directory
+  -t, --tty                            Allocate a pseudo-TTY
+      --ulimit ulimit                  Ulimit options (default [])
+  -u, --user string                    Username or UID (format: <name|uid>[:<group|gid>])
+      --userns string                  User namespace to use
+      --uts string                     UTS namespace to use
+  -v, --volume list                    Bind mount a volume
+      --volume-driver string           Optional volume driver for the container
+      --volumes-from list              Mount volumes from the specified container(s)
+  -w, --workdir string                 Working directory inside the container
+
+ +

比如我们要执行python的shell,需要添加-it参数,即:docker run -it python:3.8

+ +
$ docker run -it python:3.8 
+Python 3.8.7 (default, Dec 22 2020, 18:46:25) 
+[GCC 8.3.0] on linux
+Type "help", "copyright", "credits" or "license" for more information.
+>>> 
+
+ +

4.2 将宿主机的文件挂载到容器

+ +

docker容器与宿主机是隔离的,要想让容器内的程序能访问宿主机上的文件,需要通过-v参数将宿主机的文件挂载到容器中。

+ +

比如我们在宿主机上有一个hello.py,可以打印hello,想要在python容器中执行,就需要进行挂载。-v后还需要接两个参数,分别是宿主机的目录和容器内的目录,两者使用:分隔,路径必须都是绝对路径。

+ +

我的hello.py保存在主目录的/docker_test目录中,将这个目录挂载到容器的/docker_test目录,然后在容器内执行python /docker_test/hello.py:

+ +
$ docker run -v ~/docker_test:/docker_test python:3.8 python /docker_test/hello.py
+hello
+
+ +

4.3 容器管理

+ +

上面的服务运行之后,可以使用docker ps命令,查看运行中的容器:

+ +
$ docker ps
+CONTAINER ID     IMAGE           COMMAND                  CREATED           STATUS         PORTS                    NAMES
+ec4c86b8a163     python:3.8      "python /docker_test…"   5 seconds ago     Up 4 seconds   0.0.0.0:5000->5000/tcp   eager_wilson
+
+ +

显示的内容有下面几列:

+ + + +

要想结束容器,可以使用docker kill 容器ID命令。

+ +

自制Docker镜像

+ +

一般而言,当我们的程序开发完成后,会连同程序文件与运行环境一起制作成一个新的镜像。

+ +

要制作镜像,需要编写Dockerfile。DockeFile由多个命令组成,常用的命令有:

+ + + +

注意,Docker镜像中有一个层的概念,每执行一个RUN命令,就会创建一个层,层过多会导致镜像文件体积增大。尽量在RUN命令中使用&&连接多条shell命令,减少RUN命令的个数,可以有效减小镜像文件的体积。

+ +

5.1 自制显示文本文件内容镜像

+ +

编写cat.py,接收一个文件名,由python读取文件并显示文件的内容:

+ +
import os
+import sys
+
+input = sys.argv[1]
+
+with open(input, "r") as fp:
+    print(fp.read())
+
+ +

这个例子比较简单,缩写Dockerfile如下:

+ +
FROM python:3.8
+WORKDIR /files
+COPY cat.py /cat.py
+ENTRYPOINT ["python", "/cat.py"]
+
+ +

这个Dockerfile的含义是:

+ + + +

需要说明的是,ENTRYPOINT有两种写法:

+ +
ENTRYPOINT python /cat.py
+ENTRYPOINT ["python", "/cat.py"]
+
+ +

这里采用第二种写法,是因为我们要在外部给容器传递参数。执行命令编译Docker镜像:

+ +
docker build -t cat:1.0 .
+
+ +

这个命令中,-t的含义是目标,即生成的镜像名为hello,版本号为1.0,别忘了最后那个.,这叫到上下文路径,是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包。

+ +

这样,我们的第一个镜像就制作完成了,使用下面的命令执行它:

+ +
docker run -it -v ~/docker_test/cat/files:/files cat:1.0 test.txt
+
+ +

即可看到~/docker_test/cat/files/test.txt的内容。

+ +

5.2 用 Docker 部署一个 Python 应用

+ +

使用 Docker 部署应用的常规流程是:

+ + + +

为了演示方便,这里以一个简单的 Flask 项目为例进行讲解

+ +
项目开发
+ +
from flask import Flask
+
+# 安装依赖
+# pip3 install -U flask
+
+app = Flask(__name__)
+
+
+@app.route('/')
+def index():
+    return "测试容器部署!"
+
+
+if __name__ == '__main__':
+    app.run(host='0.0.0.0', port=8888)
+
+# 浏览器访问测试
+# http://127.0.0.1:8888/
+
+ +

项目开发完成,并在本地测试通过后就可以编写 Dockerfile 文件了

+ +
编写 Dockerfile
+ +

在项目根目录下,创建一个 Dockerfile 文件,使用上面的指令编写描述脚本

+ +

需要注意的是,这里使用「 EXPOSE 」指令暴露的端口号与入口文件定义的端口号保持一致

+ +
# Dockerfile
+
+FROM centos:7.9.2009
+RUN yum makecache fast;
+RUN yum install python3-devel python3-pip -y
+RUN pip3 install -i https://pypi.douban.com/simple flask
+COPY main.py /opt
+WORKDIR /opt
+EXPOSE 8888
+CMD ["python3","main.py"]
+
+ +
构建镜像
+ +
# 在当前文件夹下,根据Dockerfile文件构建一个镜像
+# 镜像名称:xag/my_flask_web
+# --no-cache:不使用旧的缓存进行镜像构建
+docker build --no-cache -t "xag/my_flask_web" .
+
+ +
运行镜像容器
+ +

使用 docker run 命令基于镜像运行一个容器

+ +

其中

+ + + +
# -d:后台运行
+# 宿主机(9999)映射容器内的8888(上面Dockerfile已经暴露了8888端口)
+docker run -d --name flask_web -p 9999:8888 xag/my_flask_web  
+
+ +
测试一下
+ +

最后在浏览器中,通过宿主机暴露的端口号 9999 访问项目了

+ +

访问地址:http://127.0.0.1:9999/

+ +

导入镜像到生产环境

+ +

到此,我们的镜像已经制作好了,可是,镜像文件在哪,如何在生产环境下运行呢?

+ +

刚才使用docker images命令时,已经看到了生成的镜像:

+ +
$ docker images                          
+REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
+hello               1.0                 01fe19111dc7        59 minutes ago      893MB
+python              3.8                 f5041c8ae6b1        13 days ago         884MB
+ubuntu              20.04               f643c72bc252        5 weeks ago         72.9MB
+hello-world         latest              bf756fb1ae65        12 months ago       13.3kB
+
+ +

我们可以使用docker save命令将镜像保存到指定的文件中,保存的文件是一个.tar格式的压缩文件:

+ +
docker save -o hello.tar hello:1.0
+
+ +

将hello.tar复制到生产环境的机器上,然后执行导入命令:

+ +
docker load -i hello.tar
+
+ +

就可以使用了。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/37/dfa81f915abb0055c8e7be6e93af0679bc28ce570933472f35a9f1e0e19d04 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/37/dfa81f915abb0055c8e7be6e93af0679bc28ce570933472f35a9f1e0e19d04 new file mode 100644 index 0000000000..ab2648444a --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/37/dfa81f915abb0055c8e7be6e93af0679bc28ce570933472f35a9f1e0e19d04 @@ -0,0 +1,368 @@ +I"B

主要包括使用一维卷积神经网络(1D CNN)对安装在远程风机的高速轴承中的现场传感器采集到的时间序列数据通过短时傅里叶变化之后得到的频谱峰度(Kurtosis Spectogram)进行分类,得到每个样品的类别:中等寿命级(预期寿命大于15天的轴承——状态良好)以及短寿命轴承(预期寿命小于15天的轴承——状态不良)。

+ +

原文链接:Wind Turbine High Speed Bearing Prognosis Kaggle

+ +

1. 问题描述

+ +

轴承寿命预测

+ +

本文展示了使用振动监测传感器的原始数据来预测轴承的RUL(剩余使用寿命)的新方法,这种方法也可以用于其他类型的机器元件,如齿轮、联轴器等剩余寿命的预测。

+ +

本例中使用的数据是由安装在一个远程风力涡轮机的高速轴承上的现场传感器记录的。 +该轴承是齿轮箱的一部分,负责转子和发电机之间的耦合,特别是在高速轴上(发电机一侧)。

+ +

该数据可以通过以下方式访问:

+ +
    +
  1. WindTurbineHighSpeedBearingPrognosis-Data
  2. +
+ +

记录的变量是传感器在30天内测得的加速度(单位:g),每天6秒。

+ +

2. 技术背景

+ +

该方法是基于对50个样本中的每个样本的Kurtosis Spectogram(频谱峰度)的计算,并使用1D-CNN–一维卷积神经网络将每个样本划分为两个RUL等级。中等预期寿命类,代表轴承的预期寿命大于15天(状况良好)。

+ +
    +
  1. 中等预期寿命的类别:寿命大于15天(状况良好)。
  2. +
  3. 短预期寿命类别,代表轴承的寿命期望值小于15天(状况不佳)。
  4. +
+ +

基于这个假设,寿命最短的15个被标记为短寿命类,而所有剩余的被标记为中寿命类。

+ +

数据处理流程

+ +

导入包

+ +
import numpy as np
+import matplotlib.pyplot as plt
+
+import matplotlib
+matplotlib.rcParams['mathtext.fontset'] = 'stix' 
+matplotlib.rcParams['font.family'] = 'sans-serif'
+matplotlib.rcParams['font.size'] = 10
+
+from scipy.io import loadmat # scipy加载mat文件
+from scipy.signal import stft # scipy的短时傅里叶变换
+from scipy.stats import kurtosis # kurtosis表示峰度,峰度是统计学中的一个概念,用来描述某个分布的峰值的陡峭程度
+
+from mpl_toolkits.mplot3d import Axes3D
+
+from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
+from sklearn.model_selection import train_test_split
+
+from tensorflow.keras.models import *
+from tensorflow.keras.layers import *
+from tensorflow.keras.utils import *
+
+ +

定义混淆矩阵可视化函数

+ +
def plotConfusionMatrix(dtrue,dpred,classes,\
+                        cmap = plt.cm.Blues,bsize = 1.0): # 混淆矩阵可视化函数,dtrue表示真实值,dpred表示预测值,classes表示类别,cmap表示颜色,bsize表示图像大小
+  
+    cm = confusion_matrix(dtrue,dpred,normalize = 'true') # 混淆矩阵,normalize = 'true'表示归一化
+  
+    fig,ax = plt.subplots(figsize = (np.shape(classes)[0] * 1.25 * bsize,\
+                                     np.shape(classes)[0] * 1.25 * bsize))
+  
+    im = ax.imshow(cm,interpolation = 'nearest',cmap = cmap) # interpolation = 'nearest'表示插值方式,nearest表示最近邻插值,cm表示混淆矩阵,cmap表示颜色
+  
+    ax.set(xticks = np.arange(cm.shape[1]),\
+           yticks = np.arange(cm.shape[0]),\
+           xticklabels = classes,\
+           yticklabels = classes,\
+           ylabel = 'True Efficiency',\
+           xlabel = 'Predicted Efficiency')
+  
+    plt.setp(ax.get_xticklabels(),rotation = 90,ha = 'right',\
+             rotation_mode = 'anchor') # ha表示水平对齐方式,rotation_mode表示旋转模式,anchor表示锚点;setp表示设置属性
+
+    fmt = '.2f' # 保留两位小数
+
+    thresh = cm.max() / 2.0 
+  
+    for i in range(cm.shape[0]):
+        for j in range(cm.shape[1]):
+            ax.text(j,i,format(cm[i,j],fmt),ha = 'center',va = 'center',\
+                    color = 'white' if cm[i,j] > thresh else 'black') # ha表示水平对齐方式,va表示垂直对齐方式,j表示列,i表示行
+  
+    fig.tight_layout()
+  
+    return ax
+
+ +

查看数据

+ +

扫描频率为97656HZ,采样的总共时长为6s,所以采样点的个数为

+ +
fname = !ls './input/data'
+fname = np.asarray(fname)
+
+df = []
+
+for i in fname:
+    df.append(loadmat('./input/data/' + i)['vibration'].flatten())# loadmat表示加载mat文件,flatten表示降维
+  
+df = np.asarray(df) # 转换为数组
+
+df.shape[0]
+
+
+ +

输出结果为:

+ +
50 # 表示样本数量
+
+ +

查看采样频率

+ +
fs = int(df.shape[1] / 6.0) # 采样频率
+t = np.linspace(0.0,6.0,df.shape[1]) # df.shape[1]表示df的第二维度的长度,即采样点数;t表示一个周期内,每个采样点的时间;
+
+print('Scan frequency: {:,d} Hz'.format(fs)) # {:,d}表示以逗号分隔的十进制整数,scan frequency表示扫描频率
+
+ +

输出结果为:

+ +
Scan frequency: 97,656 Hz
+
+ +

下图显示了加速度的测量值(单位:g),正如可以注意到的那样,加速度的振幅在时域上变化明显(x轴表示时间,每天6秒,50天)

+ +
plt.subplots(figsize = (12.0,6.0))
+for i in range(df.shape[0]):
+    plt.plot(t + i * 6.0,df[i],color = 'black',lw = 0.25) 
+
+plt.xlabel('Time (in s): sample = 50 days in total, 6 seconds per day')
+plt.ylabel('Acceleration (in g)')  
+plt.show()
+
+ +

加速度振幅图

+ +

尽管加速度有明显的变化,记录的数据数量(6 x 97,656 = +每天585,936个记录;总共29,286,800个)很大,再加上 测量的非同步条件,使得使用传统的神经网络来对轴承状况进行分类是不可行的。

+ +

这个问题需要一个不同的方法来将正弦波的时域变量转化为非周期性变量,从而使区分RUL等级成为可能。

+ +

Kurtosis

+ +

这个新的变量是通过短时傅里叶变换计算的Kurtosis,如下图所示的三维图。可以注意到,在正常情况下,Kurtosis是非常稳定的,无论频率如何变化(例如前五个样本),但当异常的运行条件导致数据偏离正态分布时,则Kurtosis趋于变大。

+ +
f = []
+fft = []
+
+wnd = 127
+
+for i in range(df.shape[0]):
+    u,v,w = stft(df[i],fs,nperseg = wnd,noverlap = int(0.8 * wnd),nfft = int(2.0 * wnd)) 
+    # stft表示短时傅里叶变换,fs表示采样频率;nperseg表示每段的长度,
+    # noverlap表示重叠的长度,nfft表示fft的长度
+    # u表示频率,v表示时域,w表示频谱
+    f.append(u)
+    fft.append(kurtosis(np.abs(w),fisher = False,axis = 1)) # 对频谱取绝对值,然后计算峰度
+    # fisher表示是否使用Fisher标准化,axis = 1表示沿着第二维度计算峰度
+
+f = np.asarray(f) # asarray表示将输入转换为数组,f表示频率
+fft = np.asarray(fft)
+
+fftn = (fft - fft.min()) / (fft.max() - fft.min()) # 归一化,n表示归一化后的频谱
+
+fftn = np.asarray(fftn)
+n = np.ones_like(f[0])# ones_like表示返回与输入形状相同的数组,数组元素为1
+fig = plt.figure(figsize = (10.0,10.0))
+ax = plt.axes(projection = '3d')
+ax.view_init(30,225)
+for i in range(f.shape[0]):
+    ax.plot(f[i] / 1.0e3,i * n,fftn[i],color = 'black',linewidth = 0.50) #  / 1.0e3表示将频率转换为kHz
+
+ax.set_xlabel('Frequency (kHz)')
+ax.set_ylabel('Sample')
+ax.set_zlabel('Kurtosis')
+plt.show()
+
+ +

频谱峰度图

+ +

模型训练

+ +

准备模型

+ +

Kurtosis被用作1D-CNN的输入(变量x),而与两个RUL等级相关的标签则通过变量y–中寿命期望值类(y = 0)用于前35个样本,而短寿命预期类(y = 1)用于最后15个样本。 +y被转换为分类格式,以适应神经网络的输出格式。

+ +

1D-CNN是通过使用Keras开源库的Sequential()函数定义的。

+ +
X = fftn 
+X = X.reshape(-1,f.shape[1],1) # reshape表示改变数组的形状,-1表示自动计算,1表示一维,f.shape[1]表示频率的长度
+y = np.zeros(X.shape[0],dtype = int)
+y[-15:] = 1 # y表示标签,-15表示从后往前数第15个,都是short-life expectancy class (y = 1)
+
+print('Input shape: {:d} samples x {:d} registers/sample'.format(X.shape[0],X.shape[1]))
+
+label = to_categorical(y) # to_categorical表示将标签转换为one-hot编码,one-hot编码表示将标签转换为二进制向量
+label
+
+ +
Input shape: 50 samples x 128 registers/sample
+
+ +
mdl = Sequential() # Sequential表示顺序模型
+mdl.add(Conv1D(100,6,activation = 'relu',\
+               input_shape = (f.shape[1],1))) # Conv1D表示一维卷积层,100表示卷积核的个数,6表示卷积核的大小,activation表示激活函数
+mdl.add(Conv1D(100,6,activation = 'relu'))
+mdl.add(MaxPooling1D(3))
+mdl.add(Conv1D(160,6,activation = 'relu'))
+mdl.add(Conv1D(160,6,activation = 'relu'))
+mdl.add(GlobalAveragePooling1D())
+mdl.add(Dropout(0.5))
+mdl.add(Dense(2,activation = 'softmax'))
+
+print(mdl.summary())
+
+ +

输出结果如下:

+ +

模型总结

+ +

训练模型

+ +
mdl.compile(loss = 'categorical_crossentropy',\
+            optimizer = 'adam',metrics = ['accuracy']) # compile表示编译模型,loss表示损失函数,optimizer表示优化器,metrics表示评估模型的指标
+
+ +

该模型在50%的样本上进行了训练–需要注意的是,该数据集非常有限,只有50个样本,尽管每个样本有大量的记录。

+ +

训练是基于1,000个epochs–这是为了让模型有一个良好的收敛。

+ +

代价矩阵(变量cw)被用来影响分类结果并避免与错误分类相关的错误。

+ +

当遇到很难决策的时候,将中等寿命的轴承归类为短寿命轴承,并将决定权交给维修专家,让维修专家来决定,而不是错误地将短寿命的轴承错误分类并导致机组故障。

+ +
batch,epoch = 32,1000 # batch表示每次训练的样本数,epoch表示训练的轮数
+cw = {0: 5.0,1:75.0} # cw表示类别权重
+
+Xtrain,Xtest,ytrain,ytest = train_test_split(X,label,\
+                                             random_state = 21,\
+                                             test_size = 0.50)
+# train_test_split表示将数据集分为训练集和测试集,random_state表示随机数种子,test_size表示测试集的比例为50%
+
+hist = mdl.fit(Xtrain,ytrain,batch_size = batch,\
+               epochs = epoch,validation_split = 0.2,verbose = 0,\
+               class_weight = cw)
+# fit表示训练模型,batch_size表示每次训练的样本数,epochs表示训练的轮数,validation_split表示验证集的比例,verbose表示日志显示,0表示不显示
+
+ +

训练之后,画出accracy和loss随着训练次数的变化图。我们认为,接近1的正确率说明训练收敛情况良好。

+ +
plt.subplots(1,2,figsize = (9.0,4.5),sharex = True) # sharex表示共享x轴
+plt.subplot(1,2,1)
+plt.plot(hist.epoch,hist.history['accuracy'],\
+         color = 'black',lw = 2.50)  
+# hist.epoch表示训练的轮数,hist.history['accuracy']表示训练集的准确率,可以看到随着训练的轮数增加,训练集的准确率也在增加
+plt.ylabel('1D-CNN Accuracy')
+plt.xlabel('Epoch')
+plt.subplot(1,2,2)
+plt.plot(hist.epoch,hist.history['loss'],\
+         color = 'pink',lw = 2.50)
+plt.ylabel('1D-CNN Loss')
+plt.xlabel('Epoch')
+plt.tight_layout()
+plt.show()
+
+ +

accuracy and loss

+ +

可以发现,模型的准确率高到90%。

+ +

评估模型的变现:

+ +
print(mdl.evaluate(Xtest,ytest,verbose = 2)) # evaluate表示评估模型
+
+ +

输出结果如下:

+ +
1/1 - 0s - loss: 0.4079 - accuracy: 0.9200 - 28ms/epoch - 28ms/step
+[0.4079207181930542, 0.9200000166893005]
+
+ +

分类报告显示了每个类的精度、召回率和f1-score的详细信息,包括模型的整体性能。

+ +
# print(classification_report(np.where(ytest != 0)[1],\
+#                             mdl.predict_classes(Xtest)))
+predict=mdl.predict(Xtest) 
+classes_x=np.argmax(predict,axis=1)
+print(classification_report(np.where(ytest != 0)[1],\
+                            classes_x))
+
+ +

输出结果如下:

+ +
1/1 [==============================] - 0s 37ms/step
+              precision    recall  f1-score   support
+
+           0       1.00      0.88      0.94        17
+           1       0.80      1.00      0.89         8
+
+    accuracy                           0.92        25
+   macro avg       0.90      0.94      0.91        25
+weighted avg       0.94      0.92      0.92        25
+
+ +

通过下面描述的混淆矩阵,可以更直观地显示召回率:

+ +
plotConfusionMatrix(np.argmax(ytest,axis = 1),\
+                    np.argmax(mdl.predict(Xtest),axis = 1),\
+                    ['medium','short'],\
+                    bsize = 1.75,cmap = 'binary')
+plt.title('Accuracy (RECALL)')
+plt.show()
+
+ +

召回率混淆矩阵

+ +

附录

+ +

深度学习评价标准

+ +

Confusion matrix

+ +

TP(a):实际正类预测为正类的数量;FN(b):实际正类预测为负类的数量

+ +

FP(c):实际负类预测为正类的数量;TN(d):实际负类预测为负类的数量

+ +

IoU交并比:用于判断检测出的目标是否和真值目标框匹配,即判断是tp、fp还是fn

+ +\[IoU = tp/(tp+fp+fn)\] + +

Precision 精确率(也就是查准率):

+ +\[Precision = tp /(tp+fp)\] + +

Recall召回率(也即是查全率)

+ +\[Recall = tp/(tp+fn)\] + +

P-R曲线

+ +

P-R曲线

+ +

从图中可以看出,Precision高,Recall就很难做到非常高;Recall高,Precision就很难做到很高。因此,一般画出P-R曲线发现成反比例。于是,产生问题:

+ +
    +
  1. 如何选取最合适的阈值以平衡Precision和Recall? 答案:可利用F1 score寻找Precision和Recall之间的最优平衡,即选择使得F1 score取得最大值时对应的阈值。
  2. +
+ +\[F1 = 2* (Precision * Recall)/(Precision+Recall)\] + +

频谱峰度(Kurtosis Spectogram)

+ +

谱峭度(Spectrum kurtosis,SK)的概念最先由Dwyer在1983年提出,其本质是计算每根谱线峭度值的高阶统计量。谱峭度对信号中的瞬态冲击成分十分敏感,能有效的从含有背景噪声信号中识别瞬态冲击及其在频带中的分布。由于谱峭度的复杂性、缺少一个正式的定义和一个容易理解的计算过程使其在很长时间内都未能引入到工程应用中,直到Antoni等在2006年为谱峭度做了详细的定义,并提出了基于短时傅里叶变换的SK估计器,将理论概念与实际应用联系起来,提出了Kurtogram算法,并阐述了该算法在旋转机械故障特征提取领域的具体应用过程。

+ +

轴承故障诊断之谱峭度法的一些总结

+ +

1D CNN

+ +

写不动了!!!

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/39/51da1edb40bf61f0340b95c9f09a765cedd9ed7d0eb2316e1d52f023583613 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/39/51da1edb40bf61f0340b95c9f09a765cedd9ed7d0eb2316e1d52f023583613 new file mode 100644 index 0000000000..8c19277c56 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/39/51da1edb40bf61f0340b95c9f09a765cedd9ed7d0eb2316e1d52f023583613 @@ -0,0 +1,7 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/3a/724c5f88752885bd5b422c343e1dea7350383168ad2f65751b20f48a95c0f7 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/3a/724c5f88752885bd5b422c343e1dea7350383168ad2f65751b20f48a95c0f7 new file mode 100644 index 0000000000..a657655295 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/3a/724c5f88752885bd5b422c343e1dea7350383168ad2f65751b20f48a95c0f7 @@ -0,0 +1,276 @@ +I"x.

简介

+ +

一般来说,个人主页是自己的一张名片,有些人用来记录自己的生活,有些人用来分享技术、总结自己踩过的坑,有些人用来展示实验室和研究生招生等等。其实很多类似博客园之类的都可以写文章,没有必要自己弄自己的网站。

+ +

但是如果想选择自己搭建自己的网站,想选择不一样的样式,那么不妨可以多看看大佬们的主页。

+ +

对此,我们需要满足两个条件:

+ +

第一,我们可能需要掌握一些前端的编程知识,至少我们需要学会HTML超链接文本的书写,在里面可以展示我们网站想要展示给别人的基本内容;如果需要对网页内容进行美化,则还需要学习CSS(层叠样式表)的基本知识;如果进一步还想和观众进行互动的话,可能还要学习JavaScript的知识,这个其实就跟画PPT一样,我们肯定能学会,但是能不能画好,就是另外一回事了。

+ +

第二个,就是要从一些运营商购买域名,比如说我同学haris就有自己的域名,还买了上市运营商的股票。但是主要问题是有一定学习门槛,而且我心疼钱呀呜呜呜!!!

+ +

那么,有没有一种方法,能够做到零成本、维护简单、以及不需要额外设计呢?答案是肯定的,GitHub 为我们提供了相应的 Pages 服务。这意味着,只要我们拿到别人已经做好的样式文件(别人写好了css,js等文件,拿来用就行了),再放入自己的 Markdown 文件,就能够建立简易的静态网页,从而实现零成本搭建个人主页的需求。

+ +

以下是一些我参考过的,对我研究生生涯很有帮助的个人主页:

+ +
    +
  1. 炸鸡人博客 +
      +
    1. Python系列
    2. +
    3. Cartopy系列
    4. +
    5. Matplotlib 系列
    6. +
    7. +
    +
  2. +
  3. 摸鱼的大佬
  4. +
  5. Eddy’s World +
      +
    1. Academic
    2. +
    3. Life +
        +
      1. 关于抑郁症这件事
      2. +
      3. 关于自律的一个月尝试
      4. +
      5. 小白入门系列之一:健身增肌训练
      6. +
      +
    4. +
    5. Software
    6. +
    +
  6. +
  7. 抽筋儿的话语权
  8. +
  9. Clarmy吱声
  10. +
+ +

Jekyll简介

+ +

我们首先来解决第一个问题,那就是不会一些前端语言,只会最简单的Markdown能不能写出自己的静态网站?

+ +

有滴,套模板就好了。

+ +

常用的静态网站生成器有:Jekyll,Hugo,Hexo等。Jekyll使用Ruby语言构建,是GitHub Pages默认支持的生成器;Hugo使用Go语言构建,Hexo使用NodeJS技术构建。据说Hugo速度是最快的,Hexo不同版本配置会差不少。

+ +

为什么最后会选择Jekyll生成器呢? +因为我看上的模板是这样搭建的

+ +

如何使用GitHub Page

+ +

GitHubPages 是 GitHub推出的免费静态网页托管服务,我们可以使用 GitHub 或者 Gitee 托管博客、项目官网等静态网页。它们支持以 Jekyll、Hugo、Hexo 等工具编译而成的静态资源。同时网上也存在着较多的 Jekyll Themes、Hexo Themes 、Hugo Themes 等主题资源。对于轻量的博客需求,我们只需拷贝任意一种自己喜欢的主题,然后上传至自己的仓库,就能够通过 “name.github.io” 或者 “name.gitee.io” 等域名访问自己的博客页面。

+ +

Gitee据说也可以,但是好像很容易被吞图传

+ +

当然这个不是必须的,WordPress等其他网站或者自己搞一个自己的域名也是很好的选择。但是,谁让它是免费的呢;而且还有这么多技术博客教你怎么?

+ +

使用github page创建自己的免费个人博客主要包括以下步骤,见官网步骤

+ +
    +
  1. 创建一个repository,这个repository一定要命名为username.github.io的格式。
  2. +
  3. 在本地克隆该repository。
  4. +
  5. 进入该项目文件夹并进行修改,可以多找找其他人的博客,确定真的喜欢才行。我第一次找了个二次元大佬的博客主页,后来发现不少很适合自己。
  6. +
  7. 将修改发布到repository:Add, commit, and push your changes。
  8. +
  9. 刷新一下,应该一两分钟之后就可以在自己的网站看到自己的更改。
  10. +
  11. 或者可以启动本地服务,现预览一遍可能的效果:jekyll serve然后在Server address: http://127.0.0.1:4000查看效果。
  12. +
+ +

撰写博文的过程

+ +

比如说从写一个Markdown简介开始

+ +

Jekyll 环境配置

+

安装 jekyll

+
gem install jekyll
+
+

启动本地服务

+
jekyll serve
+
+

在浏览器中打开 http://127.0.0.1:4000,就可以看到本地的网页了。

+ +

写完之后你的项目文件夹应该是怎么样的呢?

+ +

Jekyll 的核心其实是一个文本转换引擎。它的概念其实就是: 你用你最喜欢的标记语言来写文章,可以是 Markdown,也可以是 Textile,或者就是简单的 HTML, 然后 Jekyll 就会帮你套入一个或一系列的布局中。在整个过程中你可以设置URL路径, 你的文本在布局中的显示样式等等。这些都可以通过纯文本编辑来实现,最终生成的静态页面就是你的成品了。

+ +

一个基本的 Jekyll 网站的目录结构一般是像这样的:这是我写完博客之后的文件夹列表(有删减)

+ +
│  .gitignore
+│  404.html
+│  about.md
+│  archive.html
+│  feed.xml
+│  Gemfile
+│  Gemfile.lock
+│  index.html
+│  Rakefile
+│  README.md
+│  support.md
+│  tags.html
+│  _config.yml
+│  
+├─.jekyll-cache
+│  └─Jekyll
+│      └─Cache
+│          ├─Jekyll--Cache
+│          │  └─b7
+│          │          9606fb3afea5bd1609ed40b622142f1c98125abcfe89a76a661b0e8e343910
+│          │          
+│          └─Jekyll--Converters--Markdown                     
+├─code
+│  │  2022-07-11-D-Tale库介绍.ipynb
+│  │  
+│  └─.ipynb_checkpoints
+│          2022-07-11-D-Tale库介绍-checkpoint.ipynb
+│          
+├─css
+│      animate.css
+│      main.css
+│      post.css
+│      tomorrow.css     
+├─images
+│  │  avatar.jpg
+│  │  background-cover.jpg
+│              
+├─js
+│      highlight.pack.js
+│      main.js
+│      
+├─_includes
+│  │  comments.html
+│  │  external.html
+│  │  footer.html
+│  │  head.html
+│  │  new-old.html
+│  │  pagination.html
+│  │  read-more.html
+│  │  side-panel.html
+│  │  toc.html
+│  │  
+│  ├─JB
+│  │      pages_list
+│  │      posts_collate
+│  │      setup
+│  │      tags_list
+│  │      
+│  └─styles
+│          main.css
+│          
+├─_layouts
+│      default.html
+│      page.html
+│      post.html
+│      
+├─_posts
+│      2022-07-10-D-Tale库介绍.md
+│      2022-07-10-markdown简介.md
+│      2022-08-09-海表面高度数据下载.md
+│      2022-10-26-前端入门.md
+│      2022-10-27-如何使用GitHub Page搭建自己的个人网页.md
+│      
+└─_site
+    │  404.html
+    │  feed.xml
+    │  index.html
+    │  Rakefile
+    │  README.md
+    │  
+    ├─2022
+    │  ├─07
+    │  │  ├─D-Tale库介绍
+    │  │  │      index.html
+    │  │  │      
+    │  │  └─markdown简介
+    │  │          index.html
+    │  │          
+    │  └─08
+    │      └─海表面高度数据下载
+    │              index.html
+    │              
+    ├─about
+    │      index.html
+    │      
+    ├─archive
+    │      index.html
+    │      
+    ├─code
+    │      2022-07-11-D-Tale库介绍.ipynb
+    │      
+    ├─css
+    │      animate.css
+    │      main.css
+    │      post.css
+    │      tomorrow.css
+    │      
+    ├─images
+    ├─js
+    │      highlight.pack.js
+    │      main.js
+    │      
+    ├─support
+    │      index.html
+    │      
+    └─tags
+            index.html
+            
+
+ +

注意事项:

+
    +
  1. +

    发布时间:有一个凌晨,我文章写好了,也push上去了,也没发现错误,但是左等右等都不见个人主页更新这篇文章。后来才知道,github page应该托管的服务器在美国,用的是美国东部时间可能,也就是说你在今天发布的内容,至少要到中午才能看到。

    +
  2. +
  3. +

    如何支持数学公式:
    +在_layouts/post.html中,在head标签中加入以下代码:

    +
  4. +
+ +
    <!-- 数学公式 -->
+   <script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type="text/javascript"></script>
+   <script type="text/x-mathjax-config">
+       MathJax.Hub.Config({
+         tex2jax: {
+           skipTags: ['script', 'noscript', 'style', 'textarea', 'pre'],
+           inlineMath: [['$','$'], ['\\(','\\)']],
+           displayMath: [ ['$$','$$'], ['\[','\]'] ]
+         }
+       });
+   </script>
+   <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.10.2/dist/katex.min.css" integrity="sha384-yFRtMMDnQtDRO8rLpMIKrtPCD5jdktao2TV19YiZYWMDkUR5GQZR/NOVTdquEx1j" crossorigin="anonymous">
+   <script defer src="https://cdn.jsdelivr.net/npm/katex@0.10.2/dist/katex.min.js" integrity="sha384-9Nhn55MVVN0/4OFx7EE5kpFBPsEMZxKTCnA+4fqDmg12eCTqGi6+BB2LjY8brQxJ" crossorigin="anonymous"></script>
+   <script defer src="https://cdn.jsdelivr.net/npm/katex@0.10.2/dist/contrib/auto-render.min.js" integrity="sha384-kWPLUVMOks5AQFrykwIup5lo0m3iMkkHrD0uJ4H5cjeGihAutqP0yW0J6dpFiVkI" crossorigin="anonymous" onload="renderMathInElement(document.body);"></script>
+
+
+ +

然后在_config.yml中加入以下代码:

+ +
markdown: kramdown
+kramdown:
+  math_engine: mathjax
+  syntax_highlighter: rouge
+  input: GFM
+  hard_wrap: false
+  syntax_highlighter_opts:
+    disable: true
+
+ +

写完之后如何发布

+ +

写完所有代码并且在本地查看了效果之后,就可以把本地项目所做的更改push到github上了。在本地项目的根目录下,打开git bash,输入以下命令:

+ +
git add .
+git commit -m "提交信息"
+git push -u origin master
+
+
+ +

待解决功能

+ +
    +
  1. 评论功能,我之前按照教程去弄,但是好像是个韩国的网站,它给我发了注册信息,但是都被我邮箱垃圾桶照单全收了。两个月之后,我才在垃圾箱里面找到,但是那个时候我已经不知道怎么搞了,真的是“出师未捷身先死。”
  2. +
+ +

参考连接

+ +
    +
  1. Jekyll搭建个人博客 (但是这个大佬好像以及不更新了)
  2. +
  3. 用 Hugo 重新搭建博客
  4. +
  5. +
+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/3a/8794c986f3a8d3bfdc4fce3efdce91b9d53ee1d2e5ad547633496d6e35ae57 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/3a/8794c986f3a8d3bfdc4fce3efdce91b9d53ee1d2e5ad547633496d6e35ae57 new file mode 100644 index 0000000000..766db09c09 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/3a/8794c986f3a8d3bfdc4fce3efdce91b9d53ee1d2e5ad547633496d6e35ae57 @@ -0,0 +1,2 @@ +I"

主要包括列表推导式,迭代器与生成器,装饰器,类的特殊方法(魔法方法),类的装饰器等等

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/3c/4f1c9500e2a0b7a9b723e1d04afa4ede98f9659182003ccc39c1b60e883552 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/3c/4f1c9500e2a0b7a9b723e1d04afa4ede98f9659182003ccc39c1b60e883552 new file mode 100644 index 0000000000..7953831a93 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/3c/4f1c9500e2a0b7a9b723e1d04afa4ede98f9659182003ccc39c1b60e883552 @@ -0,0 +1,100 @@ +I"

1. 什么是stable diffusion?

+ +

2. 环境搭建

+ +

python3.10.6

+ +

也可以从python release页面下载: +https://www.python.org/downloads/windows/

+ +

项目地址: +stable diffusion

+ +

拉取到本地:

+ +
https://github.com/AUTOMATIC1111/stable-diffusion-webui.git
+
+ +

可能会遇到的问题:

+ +

windows下已经存在了其他的python版本,要先切换默认的python版本:

+ +
py --version # 查看当前默认的python版本
+py -0p # 查看当前系统中安装的python版本
+py -3.x -m ensurepip --default-pip # 切换默认的python版本
+
+ +

或者找到“系统属性”,点击“环境变量”,在“系统变量”中找到“Path”,在“Path”中应该已经有了两个python路径,删除其中一个python路径或者把其中想要的路径上移到最前面即可。

+ +

如果已经用了其他版本的python,要先去stable-diffusion-webui文件夹下面的venv删除所有文件再进行操作。

+ +

下载stable diffusion

+ +

运行webui-user.bat,第一次会下载所需要的whl和依赖包,可能会比较慢,耐心等待。

+ +

第一次运行出现的问题: +gfpgan安装失败

+ +

解决方案

+ + + +

如果文件路径有空格,要加上双引号。 +这里的python.exe -m表示使用python.exe这个文件来执行模块pip,也就是pip.py这个文件。

+ +

安装open_clip失败

+ +

解决方案

+ +

还有其他一些小问题,很好解决的。

+ +

比如说从github上面拉取Stability-AI +/stablediffusion出现 Failed to connect to github.com port 443: Timed out的问题,可以换国内的镜像源解决。

+ +
git clone https://hub.fgit.gq/Stability-AI/stablediffusion.git "D:/software1/stable diffusion/stable-diffusion-webui/repositories/stable-diffusion-stability-ai" 
+
+ +

运行stable diffusion

+ +

训练模型时报错:

+ +

torch.cuda.OutOfMemoryError: CUDA out of memory. Tried to allocate 128.00 MiB (GPU 0; 4.00 GiB total capacity; 2.20 GiB already allocated; 44.64 MiB free; 2.41 GiB reserved in total by PyTorch) If reserved memory is >> allocated memory try setting max_split_size_mb to avoid fragmentation. See documentation for Memory Management and PYTORCH_CUDA_ALLOC_CONF

+ +

解决方法:

+ +
    +
  1. 降低batch_size
  2. +
  3. 换显卡
  4. +
  5. repositories\stable-diffusion-stability-ai\scripts\txt2img.py +
      +
    1. parser.add_argument的–n_samples的default改为1
    2. +
    3. model.to(device)后添加 .half() 没找到这个在哪
    4. +
    +
  6. +
  7. 1
  8. +
+ +

Civitai

+ +

自己训练的照片不太满意,可以用Civitai的模型进行训练。

+ +

https://civitai.com/models/8109/ulzza

+ +

https://civitai.com/models/6424/chill

+ +

其他

+ +

txt2img只是stable diffusion最简单的功能,还可以img2img等等。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/3d/136cba488862e037674e7417600caee920ca5f5a2d3bfaeaff0ee66c2c2ece b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/3d/136cba488862e037674e7417600caee920ca5f5a2d3bfaeaff0ee66c2c2ece new file mode 100644 index 0000000000..2a4c8d5bce --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/3d/136cba488862e037674e7417600caee920ca5f5a2d3bfaeaff0ee66c2c2ece @@ -0,0 +1,2 @@ +I"?

Docker相关的概念

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/3d/5f622e82c5d1b6a4c9174156b456f72f4aa6b7438c70ee98eabcf40a5dac04 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/3d/5f622e82c5d1b6a4c9174156b456f72f4aa6b7438c70ee98eabcf40a5dac04 new file mode 100644 index 0000000000..61fe93c246 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/3d/5f622e82c5d1b6a4c9174156b456f72f4aa6b7438c70ee98eabcf40a5dac04 @@ -0,0 +1,2 @@ +I"F

libpython3.7m.so.1.0: /usr/local/lib/libpython3.7m.so.1.0

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/3f/22a0f3f3714bcf00016962d23f678eda0a91d1bf395f8cbda44deab0469901 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/3f/22a0f3f3714bcf00016962d23f678eda0a91d1bf395f8cbda44deab0469901 new file mode 100644 index 0000000000..0a4ca277a7 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/3f/22a0f3f3714bcf00016962d23f678eda0a91d1bf395f8cbda44deab0469901 @@ -0,0 +1,32 @@ +I"C

1. 什么是知识卡片

+ +

2. 如何使用ChatGPT制作知识卡片

+ +

我希望你能作为一个专业的抽认卡制作者,能够根据我提供的文本制作抽认卡。

+ +

制作抽认卡的说明:

+ + + +

请将你制作的卡片以markdown表格(问题/答案/细节)的形式输出,不要有任何额外的文字。

+ +

如果你明白,请回复:”我已经学会了如何创建抽认卡。请为我提供文本”。

+ +

3. 如何在Anki卡片中利用遗忘曲线对知识点进行周期性地复习

+ +

4. Linux知识点卡片

+ +

5. 参考文献

+ +

ChatGPT 出现后,那些靠知识记忆取胜的教育模式会被颠覆吗? - 叶峻峣的回答 - 知乎 +https://www.zhihu.com/question/583015287/answer/2901234936

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/40/b1d6a947f69dc746983941575d10a8d4006e1d59f08c07a456a97c26eac9e9 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/40/b1d6a947f69dc746983941575d10a8d4006e1d59f08c07a456a97c26eac9e9 new file mode 100644 index 0000000000..952c3c414b --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/40/b1d6a947f69dc746983941575d10a8d4006e1d59f08c07a456a97c26eac9e9 @@ -0,0 +1,11 @@ +I"

软件测试是什么

+

软件测试的一个目的是验证你的猜想-猜想软件的内部逻辑,包括第三方的模块、代码包。甚至Python的内建的数据结构是如何运行的。Tasks项目使用名为Task的数据结构,它是用namedtuple工厂函数生成的。Task用于在UI层和API层之间传递信息。

+
from collections import namedtuple
+Task = namedtuple('Task', ['summary', 'owner', 'done', 'id'])
+# 使用_new_.defaults_创建一个默认的Task实例
+Task.__new__.__defaults__ = (None, None, False, None)
+
+

运行pytest时可以指定目录和文件。如果不指定,会搜索当前目录以及其子目录中以test_开头或者已_test结尾的文件。pytest会自动搜索这些文件中的测试用例。
+rootdir: D:\pytest\Tasks\tests, +inifile: pytest.ini & tox.ini & setup.cfg

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/41/0dfea48e56f3f27ef73e2255127e031c043466b89e32fd0945c011667487ea b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/41/0dfea48e56f3f27ef73e2255127e031c043466b89e32fd0945c011667487ea new file mode 100644 index 0000000000..ae7269a59f --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/41/0dfea48e56f3f27ef73e2255127e031c043466b89e32fd0945c011667487ea @@ -0,0 +1,239 @@ +I"*"

介绍了Allure的安装,环境配置;Allure命令,配置pytest.ini文件,利用allure工具生成html报告;最后,讲了Allure的应用实例。

+ +

Allure

+ +

快速入门

+ +

安装

+ +
    +
  1. 安装allure的python插件
  2. +
+ +
pip install allure-pytest -i https://mirrors.aliyun.com/pypi/simple/
+
+

或者写入requirements.txt

+ +
allure-pytest==2.8.18
+
+ +
pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
+
+ +
    +
  1. allure工具的安装 +allure的python插件(而且生成的不是html格式的文件)依赖allure工具,所以需要先安装allure工具:
  2. +
+ +

allure需要有jdk环境,所以需要先安装jdk。

+ +
sudo apt-get update
+sudo apt-get install openjdk-8-jdk
+sudo update-alternatives --set java /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/java  # 设置环境变量
+java -version  # 显示版本信息表示安装成功
+
+ +

安装Allure

+ +

方法一:

+ +
sudo apt-get install allure
+# 查看安装路径
+whereis allure
+# allure: /usr/share/allure
+# 配置软连接或者环境变量
+sudo ln -s /usr/share/allure /usr/bin/allure
+
+ +

上述安装方式使用allure – version时候报:allure: command can not found ,需要设置allure的bin放到PATH里边。 若whereis allure找不到allure,换下边方式安装。

+ +

方法二:

+ +
curl -O https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/2.13.8/allure-commandline-2.13.8.tgz # 下载allure,文件会下载到当前目录;curl命令是linux下的下载工具,windows下可以用wget
+sudo tar -zxvf allure-commandline-2.13.8.tgz -C /opt/ # -C指定解压目录,这里解压到/opt/allure-2.13.8;一般来说,/opt目录下的文件都是安装软件的.
+sudo ln -s /opt/allure-2.13.8/bin/allure /usr/bin/allure  
+allure --version # 查看版本信息以及是否安装成功
+
+ +

配置环境(软连接好像不起作用):

+ +
vi /etc/profile
+# 在PATH后追加以下内容,保存
+export PATH=$PATH:/home/allure-2.13.8/bin
+# 使环境变量生效
+source /etc/profile
+
+

最后,配置好环境变量后记得重启下ide(很重要)!!! +其他下载地址: +https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/

+ +

https://github.com/allure-framework/allure2/releases

+ +

使用

+ +

首先要配置pytest.ini文件,告诉pytest使用allure插件;添加allure代码;运行;最后,利用allure工具生成html报告。

+ +
addopts = -s --alluredir ./report/result
+
+
+ +

添加allure代码,在testcase文件夹下面创建t_allure文件夹,新建test_allure.py文件,添加如下代码:

+ +
import pytest
+def test_1():
+    print("test 1")
+
+def test_2():
+    print("test 2")
+
+def test_3():
+    print("test 3")
+
+if __name__ == '__main__':
+    pytest.main(["allure_demo.py"])
+
+ +

运行代码,可以看到在同级report文件夹下生成了result文件夹,里边有allure相关的文件。

+ +

最后,点击terminal,在终端中输入allure generate ./report/result -o ./report/html –clean,生成html报告。

+ +
# 进入文件夹
+cd testcase/t_allure/
+ls
+# 生成html报告
+allure generate ./report/result -o ./report/html --clean
+# --clean表示清除之前的报告 -o表示输出的文件夹
+# 打开html报告,可以找到index.html文件,也可以使用allure open命令打开
+allure open ./report/html
+
+ +

Allure 命令

+ +

Title

+ +

可以自定义用例标题,标题默认为函数名。@allure.title(“用例标题”)

+ +

Description

+ +

可以添加测试的详细说明,支持Markdown语法。@allure.description(“用例详细描述”)

+ +

Feature

+ +

功能,可以理解为模块。@allure.feature(“功能模块”)

+ +

Story

+ +

Story,可以理解为子模块。@allure.story(“子模块”)

+ +

Severity

+ +

定义用例的严重程度,分为blocker、critical、normal、minor、trivial。@allure.severity(allure.severity_level.CRITICAL)

+ +

默认为normal。

+ +

只运行指定级别的用例:

+ +
pytest -s -q --alluredir=./report/result --allure-severities=critical # -s表示打印日志 -q表示简化输出;--allure-severities=critical表示只运行严重级别为critical的用例;
+
+ +

Allure.dynamic

+ +

可以动态添加测试步骤,支持Markdown语法,可以使用函数体内的数据动态生成.

+ +

@allure.dynamic.description(“测试步骤”)

+ +
@pytest.mark.parametrize("case",["case1","case2"])
+def test_4(self,case):
+    print(case)
+    allure.dynamic.title(case)
+
+ +

Allure应用

+ +

Allure测试用例

+ +

打开data文件集下面的testcase.xlsx文件,查看测试数据的具体信息,根据这个信息,对allure报告的内容进行如下设置:

+
sheet名称  feature 一级标签
+模块   story 二级标签
+用例ID+接口名称  title
+请求URL  请求类型 期望结果 实际结果描述
+
+ +
import allure
+#allure
+#sheet名称  feature 一级标签
+allure.dynamic.feature(sheet_name)
+#模块   story 二级标签
+allure.dynamic.story(case_model)
+#用例ID+接口名称  title
+allure.dynamic.title(case_id+case_name)
+#请求URL  请求类型 期望结果 实际结果描述
+desc = "<font color='red'>请求URL: </font> {}<Br/>" \
+       "<font color='red'>请求类型: </font>{}<Br/>" \
+       "<font color='red'>期望结果: </font>{}<Br/>" \
+       "<font color='red'>实际结果: </font>{}".format(url,method,expect_result,res)
+allure.dynamic.description(desc)
+
+ +

设置完成之后,每次都需要allure generate ./report/result -o ./report/html –clean来手动生成html报告,可以使用Subprocess来实现自动化生成报告。

+ +

Subprocess是Python中的一个模块,用于创建新进程,连接到它们的输入/输出/错误管道,并获取它们的返回代码。

+ +

方法:

+ + +
import subprocess
+
+
+res = subprocess.call(["ls","-l"])
+print(res) # 0代表正常退出,非0代表异常退出
+
+subprocess.call("ls -l",shell=True) # shell=True,支持命令以字符串的形式传入
+
+ +

输出结果:

+ +
总用量 4
+-rw-rw-r-- 1 hugo hugo   0  1月 30 10:00 __init__.py
+-rw-rw-r-- 1 hugo hugo 138  1月 30 10:06 subprocess_demo.py
+0
+总用量 4
+-rw-rw-r-- 1 hugo hugo   0  1月 30 10:00 __init__.py
+-rw-rw-r-- 1 hugo hugo 138  1月 30 10:06 subprocess_demo.py
+
+总用量 4
+-rw-rw-r-- 1 hugo hugo   0  1月 30 10:00 __init__.py
+-rw-rw-r-- 1 hugo hugo 138  1月 30 10:06 subprocess_demo.py
+0
+总用量 4
+-rw-rw-r-- 1 hugo hugo   0  1月 30 10:00 __init__.py
+-rw-rw-r-- 1 hugo hugo 138  1月 30 10:06 subprocess_demo.py
+
+进程已结束,退出代码0
+
+ +

Allure项目运行

+ +

在common文件夹下面的Base.py文件中,添加如下代码:(运用我们上一节学习的subprocess模块)

+ +
def allure_report(report_path,report_html):
+    """
+    生成allure 报告
+    :param report_path:
+    :param report_html:
+    :return:
+    """
+    #执行命令 allure generate
+    allure_cmd ="allure generate %s -o %s --clean"%(report_path,report_html)
+    #subprocess.call
+    log.info("报告地址")
+    try:
+        subprocess.call(allure_cmd,shell=True)
+    except:
+        log.error("执行用例失败,请检查一下测试环境相关配置")
+        raise
+
+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/41/d33f07955143a150856e5c7add6fabb245cf033ca6cc9db26a4052d2146744 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/41/d33f07955143a150856e5c7add6fabb245cf033ca6cc9db26a4052d2146744 new file mode 100644 index 0000000000..e5eea2bbbf --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/41/d33f07955143a150856e5c7add6fabb245cf033ca6cc9db26a4052d2146744 @@ -0,0 +1,2 @@ +I"C

包括yaml和Excel用例测试和数据驱动开发。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/46/f37995d212e1a756871ef2a26cf13221de45e64f899d742c619ffd51353c72 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/46/f37995d212e1a756871ef2a26cf13221de45e64f899d742c619ffd51353c72 new file mode 100644 index 0000000000..ea953c2575 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/46/f37995d212e1a756871ef2a26cf13221de45e64f899d742c619ffd51353c72 @@ -0,0 +1,530 @@ +I":

主要包括列表推导式,迭代器与生成器,装饰器,类的特殊方法(魔法方法),类的装饰器等等

+ +

列表推导式

+ +
square = []
+for i in range(10):
+    square.append(i**2)
+print(square)
+
+ +
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
+
+ +
# 利用lambda表达式重构上述代码
+# map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
+# list()函数可以将map()返回的Iterator转换为list
+Iterator = map(lambda x: x**2, range(10))
+square = list(Iterator)
+print(square)
+
+
+ +
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
+
+ +
# 使用列表生成式
+square = [x**2 for x in range(10)]
+print(square)
+
+ +
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
+
+ +
# 其他类型的列表表达式
+[x+y for x in 'ABC' for y in 'XYZ']
+
+ +
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
+
+ +
# 列表表达式中使用if语句
+[x for x in range(1, 11) if x % 2 == 0]
+
+ +
[2, 4, 6, 8, 10]
+
+ +
# 列表表达式中同时使用if语句和for语句
+[x for x in range(1, 11) if x % 2 == 0 for y in range(1, 11) if y % 3 == 0]
+
+
+ +
[2, 2, 2, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, 10, 10]
+
+ +
# 列表表达式中同时使用if语句和for语句
+print([(x,y) for x in [1,2,3] for y in [3,1,4] if x != y])
+# 等价于
+combination = []
+for x in [1,2,3]:
+    for y in [3,1,4]:
+        if x != y:
+            combination.append((x,y))
+print(combination)
+
+ +
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
+[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
+
+ +

迭代器与生成器

+ +

大多数容器对象都可以使用 for 语句,这种访问风格清晰、简洁又方便。 迭代器的使用非常普遍并使得 Python 成为一个统一的整体。 在幕后,for 语句会在容器对象上调用 iter()。 该函数返回一个定义了 __next__() 方法的迭代器对象,此方法将逐一访问容器中的元素。 当元素用尽时,__next__() 将引发 StopIteration 异常来通知终止 for 循环。

+ +
for element in [1, 2, 3, 4, 5]:
+    print(element)
+
+ +
1
+2
+3
+4
+5
+
+ +
# 可以使用next()函数来调用__next__()方法的迭代器对象
+s = 'abc'
+it = iter(s)
+it
+print(next(it))
+print(next(it))
+print(next(it))
+print(next(it))
+
+ +
a
+b
+c
+
+
+
+---------------------------------------------------------------------------
+
+StopIteration                             Traceback (most recent call last)
+
+<ipython-input-21-0603a1158441> in <module>
+      6 print(next(it))
+      7 print(next(it))
+----> 8 print(next(it))
+
+
+StopIteration: 
+
+ +
# 另外一种调用__next__()方法的方法
+n = iter([1, 2, 3])
+print(n.__next__())
+print(n.__next__())
+print(n.__next__())
+
+
+ +
1
+2
+3
+
+ +
# 第三种调用__next__()方法的方法
+n = iter([1, 2, 3])
+print(next(n))
+print(next(n))
+print(next(n))
+print(next(n))
+
+ +
1
+2
+3
+
+
+
+---------------------------------------------------------------------------
+
+StopIteration                             Traceback (most recent call last)
+
+<ipython-input-27-39a89d3766fd> in <module>
+      4 print(next(n))
+      5 print(next(n))
+----> 6 print(next(n))
+
+
+StopIteration: 
+
+ +

看过迭代器协议的幕后机制,给你的类添加迭代器行为就很容易了。 定义一个 __iter__() 方法来返回一个带有 __next__() 方法的对象。 如果类已定义了__next__(),则 __iter__() 可以简单地返回 self:

+ +
class Reverse:
+    """Iterator for looping over a sequence backwards."""
+    def __init__(self, data): 
+        # 构造函数,初始化对象,传入一个可迭代对象,并将其转换为列表
+        self.data = data
+        self.index = len(data)
+
+    def __iter__(self): # 返回对象本身
+        return self
+
+    def __next__(self): # 返回下一个元素
+        if self.index == 0:
+            raise StopIteration
+        self.index = self.index - 1
+        return self.data[self.index]
+
+ +
rev = Reverse('spam')
+iter(rev)
+for char in rev:
+    print(char)
+
+ +
m
+a
+p
+s
+
+ +

生成器

+

生成器 是一个用于创建迭代器的简单而强大的工具。 它们的写法类似于标准的函数,但当它们要返回数据时会使用 yield 语句。 每次在生成器上调用 next() 时,它会从上次离开的位置恢复执行(它会记住上次执行语句时的所有数据值)。 一个显示如何非常容易地创建生成器的示例如下:

+ +
with open('*.txt') as f:
+    for line in f:
+        print(line, end='')
+# 用生成器表达式来重构上述代码
+def read_file():
+    with open('*.txt') as f:
+        for line in f:
+            yield line
+
+ +
---------------------------------------------------------------------------
+
+OSError                                   Traceback (most recent call last)
+
+<ipython-input-33-96202d7342ea> in <module>
+----> 1 with open('*.txt') as f:
+      2     for line in f:
+      3         print(line, end='')
+
+
+OSError: [Errno 22] Invalid argument: '*.txt'
+
+ +
def reverse(data):
+    for index in range(len(data)-1, -1, -1):
+        yield data[index]
+for char in reverse('spam'):
+    print(char)
+
+ +
m
+a
+p
+s
+
+ +

可以用生成器来完成的操作同样可以用前一节所描述的基于类的迭代器来完成。 但生成器的写法更为紧凑,因为它会自动创建 iter() 和 next() 方法。

+ +

另一个关键特性在于局部变量和执行状态会在每次调用之间自动保存。 这使得该函数相比使用 self.index 和 self.data 这种实例变量的方式更易编写且更为清晰。

+ +

除了会自动创建方法和保存程序状态,当生成器终结时,它们还会自动引发 StopIteration。 这些特性结合在一起,使得创建迭代器能与编写常规函数一样容易。

+ +

装饰器

+

在一些第三方库,比如说pytest中会使用到装饰器。 +Python-装饰器(wrapper)

+ +
# 先理解函数的调用,也就是函数的传入参数可以是一个函数名
+def f1():
+    print('f1')
+def g(func):
+    print(func,type(func))
+    func()
+if __name__ == '__main__':
+    g(f1)
+
+ +
<function f1 at 0x00000248DC9A2EE0> <class 'function'>
+f1
+
+ +
import logging
+# logging.warning('Watch out!')  # will print a message to the console
+logging.basicConfig(level=logging.DEBUG)
+def foo():
+    logging.info('执行foo函数')
+    print('foo')
+def bar():
+    logging.info('执行bar函数')
+    print('bar')
+if __name__ == '__main__':
+    foo()
+    bar()
+
+
+ +
foo
+bar
+
+ +
# 每个函数都写一遍logging.info(),这样会导致代码冗余,可以写个函数
+import logging
+logging.basicConfig(level=logging.DEBUG)
+def log(func):
+ 
+    logging.info(f'执行{func.__name__}函数')
+    return func()
+
+def foo():
+    print('foo')
+
+def bar():
+    print('bar')
+if __name__ == '__main__':
+    log(foo)
+    log(bar)
+
+ +
foo
+bar
+
+ +
# 函数还是太麻烦了,可以在函数里面再写一个函数
+# python装饰器用于拓展原来函数功能的一种函数,
+# 这个函数的返回值也是一个函数。
+
+
+import logging
+logging.basicConfig(level=logging.DEBUG)
+def use_logging(func): #装饰器函数
+    def wrapper():
+        logging.info(f'执行{func.__name__}函数')
+        func()
+    return wrapper
+def foo():
+    print('foo')
+def bar():
+    print('bar')
+if __name__ == '__main__':
+    foo = use_logging(foo)
+    foo()
+    bar = use_logging(bar)
+    bar()
+
+ +
INFO:root:执行foo函数
+INFO:root:执行bar函数
+
+
+foo
+bar
+
+ +
# 这样每次调用还要声明一下,太麻烦了,可以使用@符号装饰器的知识,这样就不用每次都声明了
+import logging
+logging.basicConfig(level=logging.DEBUG)
+def use_logging(func): # 装饰器函数,func是被装饰的函数
+    def wrapper():
+        logging.info(f'执行{func.__name__}函数')
+        func()
+    return wrapper
+@use_logging    # 语法糖 @装饰器函数名 @等价于func = use_logging(func)
+# 每次调用函数的时候,都会先调用use_logging函数,然后再调用函数本身,这就是装饰器的作用
+def foo():
+    print('foo')
+@use_logging
+def bar():
+    print('bar')
+if __name__ == '__main__':
+    foo()
+    bar()
+
+ +
INFO:root:执行foo函数
+INFO:root:执行bar函数
+
+
+foo
+bar
+
+ +
# 装饰器@use_logging里面还可以加参数
+import logging
+logging.basicConfig(level=logging.DEBUG)
+def use_logging(level='info'): # 默认level级别是info
+    # 装饰器函数里面再增加一个装饰器
+    def decractor(func):
+        def wrapper(*args,**kwargs):
+            if level=='debug':
+                logging.debug(f'执行{func.__name__}函数')
+            elif level=='info':
+                logging.info(f'执行{func.__name__}函数')
+            elif level=='warning':
+                logging.warning(f'执行{func.__name__}函数')
+            elif level == 'error':
+                logging.error(f'执行{func.__name__}函数')
+            func(*args,**kwargs)
+        return wrapper
+    return decractor
+@use_logging()    
+# 每次调用函数的时候,都会先调用use_logging函数,然后再调用函数本身,这就是装饰器的作用
+def foo():
+    print('foo')
+@use_logging(level = 'debug')
+def bar():
+    print('bar')
+if __name__ == '__main__':
+    foo()
+    bar()
+
+ +
INFO:root:执行foo函数
+DEBUG:root:执行bar函数
+
+
+foo
+bar
+
+ +

类的特殊方法 魔法方法

+

在Python中以两个下划线开头的方法,initstrdoc、__new__等,被称为”魔术方法”(Magic methods)。魔术方法在类或对象的某些事件出发后会自动执行,如果希望根据自己的程序定制自己特殊功能的类,那么就需要对这些方法进行重写。

+ +
# 最常见的是在class里面的init构造器
+class Config:
+    def __init__(self,baseip,port):
+        print('__init__')
+        self.baseip = baseip
+        self.port = port
+
+# 实例化一个类
+if __name__ == '__main__':
+    config = Config(baseip='127.0.0.1',port = 3000)
+    print(config.baseip)
+    print(config.port)
+
+ +
__init__
+127.0.0.1
+3000
+
+ +
class People(object):
+    # 创建对象
+    def __new__(cls, *args, **kwargs):
+        print("触发了构造方法")
+        ret = super().__new__(cls) # 调用父类的__new__()方法创建对象
+        return ret ## 将对象返
+    # 实例化对象
+    def __init__(self, name, age):
+        self.name = name
+        self.age = age
+        print("初始化方法")
+    #  删除对象
+    #   del 对象名或者程序执行结束之后
+    def __del__(self):
+        print("析构方法,删除对象")
+
+
+if __name__ == '__main__':
+    p1 = People('xiaoming', 16)
+
+ +
触发了构造方法
+初始化方法
+析构方法,删除对象
+
+ +
# 斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13
+# 特别指出:第0项是0,第1项是第一个1。从第三项开始,每一项都等于前两项之和。
+class Fib(object):
+    def __init__(self):
+        pass
+    def __call__(self,num):
+        a,b = 0,1;
+        self.l=[]
+
+        for i in range (num):
+            self.l.append(a)
+            a,b= b,a+b
+        return self.l
+    def __str__(self):
+        return str(self.l)
+    __rept__=__str__ 
+
+f = Fib()
+print(f(10))
+
+ +
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
+
+ +

类的装饰器

+

Python小技巧:装饰器(Decorator) +

+ +

+class Foo:
+    def __init__(self,func):
+        self.func = func
+    def __call__(self,*args,**kwargs):
+        print('before __call__')
+        self.func()
+        print('after __call__')
+@Foo
+def bar():
+    print('bar')
+
+if __name__ == '__main__':
+    bar()
+
+ +
before __call__
+bar
+after __call__
+
+ +
import logging
+from functools import wraps
+
+from enum import Enum
+class Level(Enum):
+    debug = 0
+    info = 1
+    warn = 2
+    error = 3
+
+
+class UseLogging:
+
+    def __init__(self,level:Level):
+        self.level = level
+        logging.basicConfig(level=logging.DEBUG)
+
+
+    def __call__(self, func):
+        @wraps(func)
+        def decorator(*args, **kwargs):
+            if self.level == Level.debug:
+                logging.debug(f'正在执行{func.__name__}')
+            elif self.level == Level.info:
+                logging.info(f'正在执行{func.__name__}')
+            elif self.level == Level.warn:
+                logging.warning(f'正在执行{func.__name__}')
+            elif self.level == Level.error:
+                logging.error(f'正在执行{func.__name__}')
+            func(*args, **kwargs)
+        return decorator
+
+@UseLogging(level=Level.warn)
+def bar(name,**kwargs):
+    print('bar',name,kwargs)
+
+if __name__ == '__main__':
+    bar("xiaoming",k = 10)
+
+ +
WARNING:root:正在执行bar
+
+
+bar xiaoming {'k': 10}
+
+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/48/93a991101cf0a2ed76ef1aff2ac2d8f5f620f57f6858fe9bba188a8f8d1fce b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/48/93a991101cf0a2ed76ef1aff2ac2d8f5f620f57f6858fe9bba188a8f8d1fce new file mode 100644 index 0000000000..de5b2e7808 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/48/93a991101cf0a2ed76ef1aff2ac2d8f5f620f57f6858fe9bba188a8f8d1fce @@ -0,0 +1,96 @@ +I"

代码

+ +
# 添加当前时间信息
+echo "当前时间为$(date)" >> output.txt
+
+# 会输出包含 "model name" 字符串的行,即 CPU 型号的信息
+model_name=$(cat /proc/cpuinfo | grep "model name" | head -n 1 | cut -d ":" -f 2 | xargs)
+echo "CPU型号信息为$model_name" >> output.txt
+#  查看内核核心数量
+cpucores=$(cat /proc/cpuinfo | grep "cpu cores" | head -n 1 | cut -d ":" -f 2 | xargs)
+echo "内核核心数量为$cpucores" >> output.txt
+# 查看系统 CPU 使用情况
+echo "系统 CPU 使用情况如下:" >> output.txt
+iostat -c | awk '{if(NR==4){print "CPU繁忙度: "$1"%"} }' >> output.txt
+iowait=$(iostat -c | awk '{if(NR==4){print $4} }')
+if [ $(echo "$iowait >=60" | bc -l) -eq 1 ]; 
+then
+    echo "CPU等待输入输出完成时间的百分比为$iowait%,过高,表示硬盘存在I/O瓶颈。" >> output.txt
+else
+    echo "CPU等待输入输出完成时间的百分比为$iowait%,正常。" >> output.txt
+fi
+# 如果%idle值高,表示CPU较空闲
+# 如果%idle值持续低于10,表明CPU处理能力相对较低,系统中最需要解决的资源是CPU。
+idle=$(iostat -c | awk '{if(NR==4){print $6} }')
+if [ $(echo "$idle >= 90" | bc -l) -eq 1 ]; 
+then
+    echo "CPU较空闲,%idle值为$idle" >> output.txt
+elif [ $(echo "$idle < 10" | bc -l) -eq 1 ]; 
+then
+    echo "CPU处理能力相对较低,idle值为$idle%" >> output.txt
+fi
+
+
+# 输出系统总内存
+MemTotal=$(cat /proc/meminfo | grep "MemTotal" | cut -d ":" -f 2 | xargs)
+echo "系统总内存为$MemTotal" >> output.txt
+# 查看系统空闲内存大小
+MemFree=$(cat /proc/meminfo | grep "MemFree" | cut -d ":" -f 2 | xargs)
+echo "系统空闲内存为$MemFree" >> output.txt
+# Cached:分配给文件缓冲区的内存(比如未保存的文件就是写到该缓冲区)
+Cached=$(cat /proc/meminfo | grep "Cached" | head -n 1 | cut -d ":" -f 2 | xargs)
+echo "分配给文件缓冲区的内存(比如未保存的文件就是写到该缓冲区)为$Cached。" >> output.txt
+
+# 查看磁盘信息
+echo "磁盘容量信息如下:" >> output.txt
+df -h | sort -k 4 -hr | grep "文件系统" >> output.txt
+df -h | sort -k 4 -hr | grep -v "文件系统" | grep -v "tmpfs" >> output.txt
+
+# 查看网络
+PingInfo=$(ping -c 5 www.baidu.com | grep "bytes from" | head -n 1 )
+echo "通过ping访问百度得到的信息如下:\n $PingInfo">> output.txt
+PingStatics=$(ping -c 5 www.baidu.com | grep "1 packets transmitted" | head -n 1 )
+echo "$PingStatics">> output.txt
+
+rtt=$(ping -c 5 www.baidu.com | grep "rtt" | cut -d "=" -f 2 | head -n 1 )
+rttmin=$(echo $rtt | cut -d "/" -f 1)
+rttavg=$(echo $rtt | cut -d "/" -f 2)
+rttmax=$(echo $rtt | cut -d "/" -f 3)
+rttmdev=$(echo $rtt | cut -d "/" -f 4)
+echo "网络最小时延为$rttmin ms" >> output.txt
+echo "网络平均时延为$rttavg ms" >> output.txt
+echo "网络最大时延为$rttmax ms" >> output.txt
+echo "网络时延平均偏差为$rttmdev" >> output.txt
+
+# perf性能瓶颈分析,查看哪些进程占用CPU较高
+# 安装命令
+# apt-get install linux-tools-$(uname -r) linux-tools-generic -y
+# https://blog.51cto.com/liuzhengwei521/2360430
+#echo "CPU性能瓶颈分析如下:" >> output.txt
+#perf top -b -p $(pidof nginx) >> output.txt
+
+
+# 查看系统负载
+# https://blog.51cto.com/ghostwritten/5344990
+# 对CPU进行压力测试
+#cpu_number=$(grep -c 'processor' /proc/cpuinfo)
+#创建8个stress进程和100个io进程,持续时间600秒,模拟CPU在用户态和内核态总使用率达到100%的场景。
+#stress -c $cpu_number -i 100 --verbose --timeout 600
+
+# 对内存进行压力测试
+# 下面例子,就是创建5个进程,同时去malloc分配内存,并且保持100s后再释放内存:
+# stress --vm 5 --vm-bytes 1G --vm-hang 100 --timeout 100s
+# stress 测试内存的时候,--vm-bytes 1G --vm-hang 100 这里2个参数是关键!!!!
+# --vm-bytes 表示malloc分配多少内存
+# --vm-hang 表示malloc分配的内存多少时间后在free()释放掉
+# --vm 指定进程数量,分配的内存总数为vm*vm-bytes
+# --timeout 持续时间
+
+# 对磁盘进行压力测试,并输出结果到文件
+# stress --hdd 2 --hdd-bytes 3G --timeout 200
+# -hdd forks 产生多个执行write()函数的进程
+# --hdd-bytes bytes 指定写的Bytes数,默认是1GB
+
+ +

结果

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/49/ce37d440d936db2308420be070d0c64679dadec31486ee111f6b89d48b5950 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/49/ce37d440d936db2308420be070d0c64679dadec31486ee111f6b89d48b5950 new file mode 100644 index 0000000000..6be885074f --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/49/ce37d440d936db2308420be070d0c64679dadec31486ee111f6b89d48b5950 @@ -0,0 +1,3 @@ +I".

联系方式

+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/49/f3c7935296cdcfc4c39dfc7f4189b0ce084e1df0d42352b911e90d4332353b b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/49/f3c7935296cdcfc4c39dfc7f4189b0ce084e1df0d42352b911e90d4332353b new file mode 100644 index 0000000000..48010c65a6 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/49/f3c7935296cdcfc4c39dfc7f4189b0ce084e1df0d42352b911e90d4332353b @@ -0,0 +1,11 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/4a/9c8389ea75cf48fa4b027a946219f65753e2211d90b4b1c991d799bf43c2d6 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/4a/9c8389ea75cf48fa4b027a946219f65753e2211d90b4b1c991d799bf43c2d6 new file mode 100644 index 0000000000..e65163f4cf --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/4a/9c8389ea75cf48fa4b027a946219f65753e2211d90b4b1c991d799bf43c2d6 @@ -0,0 +1,16 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/4b/a388856166733cdfceec511f069df53f4e95640ba363ac2d0d74b7276c6cef b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/4b/a388856166733cdfceec511f069df53f4e95640ba363ac2d0d74b7276c6cef new file mode 100644 index 0000000000..2eb89e0221 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/4b/a388856166733cdfceec511f069df53f4e95640ba363ac2d0d74b7276c6cef @@ -0,0 +1,183 @@ +I"

1. 数据来源

+ +

kaggle-Wind Farms

+ +

2. 数据探索

+ +
import numpy as np # linear algebra
+import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
+import matplotlib.pyplot as plt
+from mpl_toolkits.basemap import Basemap
+
+# Input data files are available in the "../input/" directory.
+# For example, running this (by clicking run or pressing Shift+Enter) will list the files in the input directory
+
+from subprocess import check_output
+print(check_output(["ls", "../input"]).decode("utf8")) # ls表示列出当前目录下的文件,check_output表示执行命令,decode表示解码
+
+wtfile='../input/windTurbines.csv'
+wtdat=pd.read_csv(wtfile)
+print(wtdat.head())
+
+

结果如下:

+ +
   FID  unique_id                               site_name  total_turb  \
+0    0        982                  unknown Gilliam County           2   
+1    1       1065                  unknown Gilliam County           2   
+2    2       1895                     Banner Wind Project           2   
+3    3       1897                     Banner Wind Project           2   
+4    4       2608  unknown Tehachapi Wind Resource Area 1         549   
+
+     on_year year_range  on_year_s    manufac    model type_tower  ...  \
+0    unknown    unknown     -99999    unknown  unknown    unknown  ...   
+1    unknown    unknown     -99999    unknown  unknown    unknown  ...   
+2       2008         no       2008  Entegrity     EW50    trestle  ...   
+3       2008         no       2008  Entegrity     EW50    trestle  ...   
+4  1982_1990        yes       1982    unknown  unknown   monopole  ...   
+
+  conf_attr  conf_loc           WENDI_name  EIA_name  FAA_jdate   FAA_AGL  \
+0         0         0              unknown   unknown    2013192    124.09   
+1         0         0              unknown   unknown    2013192    124.09   
+2         2         2  Banner Wind Project   unknown    2009065     38.72   
+3         2         2  Banner Wind Project   unknown    2009065     38.72   
+4         0         2              unknown   unknown     -99999 -99999.00   
+
+     FAA_ORS        image_name  image_year  \
+0  41-020577  Bing Maps Aerial     unknown   
+1  41-020578  Bing Maps Aerial     unknown   
+2  02-020064  Bing Maps Aerial     unknown   
+3  02-020070  Bing Maps Aerial     unknown   
+4    unknown              NAIP        2012   
+
+                                            comments  
+0  FAA lists as Dismantle, google temporal analys...  
+1  FAA lists as Dismantle, google temporal analys...  
+2  trestle turbines removed, new monopoles instal...  
+3  trestle turbines removed, new monopoles instal...  
+4                                                     
+
+[5 rows x 32 columns]
+
+ +

数据描述如下:

+ +

字段名称 解释说明

+ +

FID 文件编码ID

+ +

unique_id 唯一识别码

+ +

site_name 地点名称

+ +

total_turb 发电机数量

+ +

on_year 服役时间

+ +

year_range 服役时间区间

+ +

on_year_s 服役开始时间

+ +

manufac 制造商

+ +

model 型号

+ +

type_tower tower的种类

+ +

decommiss 是否退役

+ +

MW_turbine 电机的MW

+ +

total_cpcy 总的cpcy

+ +

total_ht 总的高度

+ +

tower_h tower的高度

+ +

blade_l 叶片的长度

+ +

rotor_dia 电机转子的直径

+ +

rotor_s_a 电机转子的面积

+ +

lat_DD 纬度

+ +

long_DD 经度

+ +

state 所属的州

+ +

county 所属的郡

+ +

conf_attr conf的attr

+ +

conf_loc conf的位置

+ +

WENDI_name WENDI的名称

+ +

EIA_name EIA的名称

+ +

FAA_jdate FAA的注册日期

+ +

FAA_AGL FAA的AGL

+ +

FAA_ORS FAA的ORS

+ +

image_name 照片名称

+ +

image_year 照片年份

+ +

comments 评论

+ +

3. 数据可视化

+ +

在可视化之前,应该还需要对数据进行预处理,比如去除缺失值,去除重复值等等。

+ +
#% setup the map
+fig,ax=plt.subplots(figsize=(20,10))
+
+c1=(-127.6+66.1)/2
+c2=(50.5+23.2)/2
+m = Basemap(resolution='i', # c, l, i, h, f or None
+            projection='merc',
+            lat_0=c2, lon_0=c1,
+            llcrnrlon=-127.6, llcrnrlat= 23.2, urcrnrlon=-66.1, urcrnrlat=50.5)
+# lat_0表示地图中心的纬度,lon_0表示地图中心的经度
+# llcrnrlon表示西经,llcrnrlat表示南纬,urcrnrlon表示东经,urcrnrlat表示北纬
+
+# draw the map
+m.drawmapboundary(fill_color='#46bcec') # 设置地图边界颜色, #46bcec是蓝色
+m.fillcontinents(color='#f2f2f2',lake_color='#46bcec') 
+# 设置陆地颜色,#f2f2f2是灰色,lake_color是湖泊颜色,#46bcec是蓝色
+m.drawcoastlines(linewidth=0.5)
+m.drawcountries(linewidth=0.5)
+m.drawstates(linewidth=0.5)
+x,y=m(np.array(wtdat['long_DD']),np.array(wtdat['lat_DD']))
+
+# plot each turbine with marker size based on machine size
+
+# Check the number of missing values in 'MW_turbine'
+print("Number of missing values in 'MW_turbine':", wtdat['MW_turbine'].isnull().sum())
+
+
+mx=wtdat['MW_turbine'].max() # 最大的MW,MW_turbine表示电机的MW
+for i in range(len(wtdat)):
+    if wtdat['MW_turbine'].loc[i]>0:
+        m.plot(x[i],y[i],'o',
+            color='red',
+            markersize=wtdat['MW_turbine'].loc[i]/mx*15,
+            alpha=0.25,
+            markeredgecolor='none')
+    else:
+        m.plot(x[i],y[i],
+            'x',
+            color='brown') # x表示叉号
+
+plt.show()    
+fig.savefig('WindTurbineMap.png',orientation='landscape',dpi=600)
+
+ +

这段代码的含义是,首先设置地图的中心点,然后绘制地图,最后绘制风力发电机的位置,其中地图上的散点的大小是根据风力发电机的MW来决定的。

+ +

运行结果如下:

+ +

20230406091501

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/4d/66527a1c13334114e501c3fc4ce1170ffaaf00f7c8791b1cb51728413e91c0 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/4d/66527a1c13334114e501c3fc4ce1170ffaaf00f7c8791b1cb51728413e91c0 new file mode 100644 index 0000000000..838451edb5 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/4d/66527a1c13334114e501c3fc4ce1170ffaaf00f7c8791b1cb51728413e91c0 @@ -0,0 +1,283 @@ +I".

1. pytest简介

+

Pytest 是一个比较成熟且功能完备的 Python 测试框架。其提供完善的在线文档,并有着大量的第三方插件和内置帮助,适用于许多小型或大型项目。Pytest 灵活易学,打印调试和测试执行期间可以捕获标准输出,适合简单的单元测试到复杂的功能测试。还可以执行 nose, unittest 和 doctest 风格的测试用例,甚至 Django 和 trial。支持良好的集成实践, 支持扩展的 xUnit 风格 setup,支持非 python 测试。支持生成测试覆盖率报告,支持 PEP8 兼容的编码风格。

+ +

2. pytest安装

+
pip install pytest
+
+ +

3. pytest基本使用

+

3.1 pytest命令行参数

+
py.test [options] [file_or_dir] [file_or_dir] [...]
+
+

3.1.1 常用参数

+
py.test -h,--help # 查看命令行和配置文件帮助
+pytest --markers # 查看所有的标记标记
+
+ +
-l,--showlocals #在traceback中显示本地变量
+-q,--quiet #减少输出
+--collect-only:只收集测试用例,不执行测试用例
+-v,--verbose #详细输出
+# 调试输出
+-x:遇到用例失败后,停止执行后面的用例
+--maxfail=num:最多允许num个测试用例失败,后面的用例不再执行
+--pdb:当测试用例失败时,进入调试模式
+--tb=style:指定traceback的输出风格
+    - long:完整的traceback信息
+    - short:只显示每个失败用例的第一行信息
+    - no:不显示traceback信息
+    - native:使用C语言的格式化1输出traceback信息
+-m MARKEXPR:只运行标记了指定标记的用例
+
+ +

3.2 执行选择用例

+ +
# 执行单个模块中的全部用例
+pytest test_module.py
+# 执行单个类中的全部用例
+pytest test_module.py::TestClass
+# 执行单个方法
+pytest test_module.py::TestClass::test_method
+# 执行指定module中的某个测试函数
+pytest test_module.py::test_func
+
+# 执行指定路径下的全部用例
+pytest test_dir/
+# 执行字符串匹配的用例
+pytest -k "string_expression"
+# 执行标记为smoke的用例
+pytest -m smoke
+# 执行标记为smoke且用例名中包含add的用例
+pytest -k "add and smoke"
+# 执行标记为smoke或用例名中包含add的用例
+pytest -k "add or smoke"
+# 导入模块,使用其文件系统中的路径来查找和执行用例,执行pkg包中的全部用例
+pytest --pyargs pkg # pyargs参数,pkg是包名
+
+

3.3 断言

+ +

通常情况下使用 assert 语句就能对大多数测试进行断言。对于异常断言,可以使用上下文管理器 pytest.raises

+ +
# 断言异常,可以使用上下文管理器**pytest.raises**
+def test_divide_by_zero():
+    with pytest.raises(ZeroDivisionError):
+        1 / 0 # 在这里抛出异常,断言成功
+# 还可以使用pytest.raises的context manager的value属性来获取异常信息
+def test_divide_by_zero():
+    with pytest.raises(ZeroDivisionError) as excinfo: #excinfo是异常信息
+        1 / 0
+    assert 'division by zero' in str(excinfo.value)
+# 捕捉异常信息还包括
+def test_divide_by_zero():
+    with pytest.raises(ZeroDivisionError,message=" integer division or modulo by zero"):
+        1 / 0
+# 对于警告断言,可以使用pytest.warns(上下文管理器):
+# UserWarning是警告类型,match是匹配的字符串
+with pytest.warns(UserWarning,match='must be 0 or None'):
+    warnings.warn("my warning", UserWarning) 
+with pytest.warns(RuntimeWarning):
+    warnings.warn("my warning", RuntimeWarning) 
+
+

对于自定义的assert比较断言,还可以通过在conftest.py中定义pytest_assertrepr_compare函数来实现:

+ +

+# content of test_foocompare.py
+class Foo:
+    def __init__(self, val):
+         self.val = val
+
+    def __eq__(self, other):
+        return self.val == other.val
+
+def test():
+    assert 1 == 1
+
+def test_compare():
+    f1 = Foo(1)
+    f2 = Foo(2)
+    f3 = Foo(1)
+    assert f1 == f3
+    assert f1 == f2
+
+
+# content of conftest.py
+def pytest_assertrepr_compare(op, left, right):
+    from test_foocompare import Foo
+    if isinstance(left, Foo) and isinstance(right, Foo) and op == "==":
+        return ['Comparing Foo instances:', 'vals: %s != %s' % (left.val, right.val)]
+
+ +

如果需要手动设置断言失败的信息,可以使用pytest.fail函数:

+ +
def test_fail():
+    pytest.fail("manual fail")
+
+def test_sys_version():
+    # skip test if not running on Python 3.x
+    if sys.version_info < (3, 0):
+        pytest.fail("requires python3")
+
+ +

3.4 跳过用例

+ +
# 跳过用例
+@pytest.mark.skip(reason="no way of currently testing this")
+def test_function():
+    pass
+# 跳过用例,如果条件为真
+@pytest.mark.skipif(sys.version_info < (3, 0), reason="requires python3")
+def test_function():
+    pass
+# 跳过用例,如果条件为假
+@pytest.mark.skipif(sys.version_info >= (3, 0), reason="requires python3")
+def test_function():
+    pass
+
+ +

使用 pytest.skippytest.xfail 能够实现跳过测试的功能,skip 表示直接跳过测试,而 xfail 则表示存在预期的失败,但两者的效果差不多:

+ +
def test_skip_and_xfail():
+    if sys.version_info[0] < 3:
+        pytest.skip("requires python3") 
+    else:
+        pytest.xfail("xfail on python3") 
+
+ +

pytest.importorskip 函数可以用来跳过测试,如果导入模块失败:

+ +
# 跳过用例,如果导入模块失败
+def test_importorskip():
+    pytest.importorskip("this_module_does_not_exist")
+# 要求导入的模块的版本号大于等于指定的版本号
+def test_importorskip():
+    pytest.importorskip("this_module_does_not_exist", minversion="1.0")
+
+

断言近似相等时,可以使用pytest.approx函数:

+ +
def test_approx():
+    assert 0.1 + 0.2 == pytest.approx(0.3)
+def test_approx2():
+    assert 2.2==pytest.approx(2.3,rel=0.1) # rel是相对误差
+
+ +

3.5 conftest.py

+

从广义理解,conftest.py是一个配置文件,可以用来配置pytest的运行环境,从狭义理解,conftest.py是一个pytest的插件,可以用来定义pytest的fixture、hook函数、命令行参数等。conftest.py文件可以放在测试用例所在的目录下,也可以放在测试用例所在目录的上一级目录下,如果放在上一级目录下,那么该目录下的所有子目录都会加载该conftest.py文件。

+ +

conftest.py文件功能如下:

+ + +
# content of conftest.py
+import pytest
+
+def pytest_addoption(parser):
+    parser.addoption("--full", action="store_ture",
+        help="run full test")
+
+# content of test.py
+@pytest.mark.skipif(not pytest.config.getoption("--runslow"))
+def test_func_slow_1():
+    """当在命令行执行 --runslow 参数时才执行该测试"""
+    print 'skip slow'
+
+ +

3.6 Fixture

+

Fixture 是 pytest 特有的功能,它用 pytest.fixture 标识,定义在函数前面。在编写测试函数的时候,可以将此函数名称做为传入参数,pytest 将会以依赖注入方式,将该函数的返回值作为测试函数的传入参数。

+ +
@pytest.fixture
+def login():
+    print("登录操作")
+    username
+    return username
+def test_soso(login):
+    print(f"搜索{login}")
+def test_cart(login):
+    print(f"添加购物车{login}")
+
+ +

fixture 的作用域 fixture 的作用域默认是函数级别的,也就是说,每个测试用例都会执行一次 fixture 函数。如果想要在多个测试用例中使用 fixture 函数的返回值,可以将 fixture 的作用域设置为模块级别.

+ + + +

3.7 Markers

+ +

marker 是 pytest 特有的功能,它用于给测试用例打标签,可以用于过滤测试用例,也可以用于给测试用例分类。

+ +
# 跳过测试
+@pytest.mark.skip(reason="no way of currently testing this")
+# 满足某个条件时跳过测试
+@pytest.mark.skipif(sys.version_info < (3, 3), reason="requires python3.3+")
+# 期望测试失败
+@pytest.mark.xfail(condition, reason, run=True, raises=None, strict=False)
+# 参数化测试函数,给测试用例传入参数,供运行时填充到测试中
+@pytest.mark.parametrize(argnames, argvalues, indirect=False, ids=None, scope=None)
+# 执行某一类测试用例
+pytest -m smoke
+# 测试顺序
+@pytest.mark.run(order=1)
+# 让测试尽早地被执行
+@pytest.mark.tryfirst
+# 让测试尽晚地被执行
+@pytest.mark.trylast
+
+
+ +

3.8 生成测试报告

+ +

3.9 第三方插件

+ +

pytest-randomly: 测试顺序随机 +
+pytest-ordering: 测试顺序控制 +
+pytest-xdist: 分布式测试 +
+pytest-cov: 生成测试覆盖率报告 +
+pytest-rerunfailures: 失败重试 +
+pytest-timeout: 超时测试,允许在命令行指定超时时间,或者直接在测试代码中标注超时时间。 +
+pytest-repeat: 重复测试(可以使用–count参数指定重复次数) +
+pytest-instafail: 测试失败时立即停止,查看错误的详细信息。 +
+pytest-sugar: 优化测试报告,显示色彩和进度条 +
+pytest-html: 为测试生成HTML报告

+
pytest --html=report.html
+
+


+ +

静态分析用的插件

+ +

pytest-pycodestyle:

+ +


+pytest-pep8: 检测代码是否符合 PEP8 规范 +
+pytest-flakes: 检测代码风格

+ +


+ +

Web开发用的插件: +Web项目有特定的测试逻辑,pytest也不能让测试变简单,但是有几个插件可以帮忙。 +
+pytest-django: 用于测试 Django 项目,Django是很流行的基于Python的web开发框架,它本身包含用于测试的hook函数,允许测试Django应用的各个组件,Django测试使用的是unittest; +
+pytest-selenium:借助浏览器完成自动化测试,比如说启动一个web浏览器,打开网址URL,运行web应用; +
+pytest-flask:测试Flask应用,Flask是一个轻量级的web框架,pytest-flask插件提供了一组很有用的fixture来帮助Flask应用;

+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/4e/f47eab595eb52d9e12b1a2f2bc8988c787f0c8155dd59dce1dbef6e4fda321 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/4e/f47eab595eb52d9e12b1a2f2bc8988c787f0c8155dd59dce1dbef6e4fda321 new file mode 100644 index 0000000000..b35e59811a --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/4e/f47eab595eb52d9e12b1a2f2bc8988c787f0c8155dd59dce1dbef6e4fda321 @@ -0,0 +1,8 @@ +I"2 + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/4f/65da3016c9f3f4ad88467e1d7f66a4ae118bb8ab24683c709623ae3e05ece0 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/4f/65da3016c9f3f4ad88467e1d7f66a4ae118bb8ab24683c709623ae3e05ece0 new file mode 100644 index 0000000000..ad1ff6dd1d --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/4f/65da3016c9f3f4ad88467e1d7f66a4ae118bb8ab24683c709623ae3e05ece0 @@ -0,0 +1,9 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/52/5773bd3b62123de1f97859b66c64fd7de4f6efda159fa1183c8d91de7e6780 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/52/5773bd3b62123de1f97859b66c64fd7de4f6efda159fa1183c8d91de7e6780 new file mode 100644 index 0000000000..b054c35895 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/52/5773bd3b62123de1f97859b66c64fd7de4f6efda159fa1183c8d91de7e6780 @@ -0,0 +1,2 @@ +I"U

Excel参数化之参数化运行

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/52/7aa5512bd1ee1e0387def21aad45c79540e24818a86513295ba17bccf2fe42 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/52/7aa5512bd1ee1e0387def21aad45c79540e24818a86513295ba17bccf2fe42 new file mode 100644 index 0000000000..ac98d1993d --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/52/7aa5512bd1ee1e0387def21aad45c79540e24818a86513295ba17bccf2fe42 @@ -0,0 +1,14 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/53/7fcffef9ff8b359b4f9a7fa30b4b7d6e1b1e8328d379972bbd53bfe0d18ae7 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/53/7fcffef9ff8b359b4f9a7fa30b4b7d6e1b1e8328d379972bbd53bfe0d18ae7 new file mode 100644 index 0000000000..586f59976f --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/53/7fcffef9ff8b359b4f9a7fa30b4b7d6e1b1e8328d379972bbd53bfe0d18ae7 @@ -0,0 +1,23 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/54/294d345a050b453de9936be52c9c058a8f7b35aa40b4278545852932f3ecf4 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/54/294d345a050b453de9936be52c9c058a8f7b35aa40b4278545852932f3ecf4 new file mode 100644 index 0000000000..551c8841f4 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/54/294d345a050b453de9936be52c9c058a8f7b35aa40b4278545852932f3ecf4 @@ -0,0 +1,2 @@ +I"

主要包括Yaml文件的语法规则,安装方法,Yaml的编写规则,读取方法,Yaml的封装,以及如何在config文件夹下面简历config.yaml配置文件以实现测试用例和测试数据分离。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/54/82915f099e32f6acec059c1b2eda732c2023b9bef2389fed011cdf2ff51935 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/54/82915f099e32f6acec059c1b2eda732c2023b9bef2389fed011cdf2ff51935 new file mode 100644 index 0000000000..61aa482a8c --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/54/82915f099e32f6acec059c1b2eda732c2023b9bef2389fed011cdf2ff51935 @@ -0,0 +1,327 @@ +I"mB

高效的函数

+ +

函数是 Python 的头等对象

+ +

Python 程序中的所有数据都是由对象或对象之间的关系来表示的。①字符串、列表和模块等都是对象。Python 中的函数也不例外,同样是对象。

+ +

由于 yell 函数是 Python 中的一个对象,因此像任何其他对象一样,也可以将其分配给另一 +个变量:

+ +
def yell(text):
+    return text.upper()+"!"
+bark = yell # 这一行没有调用函数,而是获取 yell 引用的函数对象,再创建一个指向该对象的名称 bark。
+# 现在调用 bark 就可以执行相同的底层函数对象
+bark('hello') # 'HELLO!'
+
+ +

此时,删除 yell 的引用,不会影响 bark 的引用:

+ +
del yell
+yell('hello') # NameError: name 'yell' is not defined
+bark('hello') # 'HELLO!'
+
+ +

Python在创建函数时为每个函数附加了一个用于调试的字符串标识符,使用函数的 name 属性可以访问该标识符:

+ +
bark.__name__ # 'yell'
+
+ +

虽然函数的__name__仍然是 yell,但已经无法用这个名称在代码中访问函数对象。名称标识符仅仅用来辅助调试,指向函数的变量和函数本身实际上是彼此独立的。

+ +

函数可以传递给其他函数

+ +

由于函数是对象,因此可以将其作为参数传递给其他函数。将函数对象作为参数传递给其他函数的功能非常强大,可以用来将程序中的行为抽象出来并传递出去。

+ +
def greet(func):
+    greeting = func("Hi, I am a Python program")
+    print(greeting)
+greet(bark) # 'HI, I AM A PYTHON PROGRAM!'
+
+

向 greet 函数传递 bark 函数,greeting = bark(“Hi, I am a Python program”) = “HI, I AM A PYTHON PROGRAM!”,然后打印出来。

+ +

能接受其他函数作为参数的函数被称为高阶函数。高阶函数是函数式编程风格中必不可少的一部分。

+ +

Python 中具有代表性的高阶函数是内置的 map 函数。map 接受一个函数对象和一个可迭代对象,然后在可迭代对象中的每个元素上调用该函数来生成结果。map 返回一个迭代器,可以使用 list 函数将其转换为列表。

+ +
list(map(bark,['hello','hey','hi'])) # ['HELLO!', 'HEY!', 'HI!']
+
+ +

map 遍历整个列表并将 bark 函数应用于每个元素,然后将结果放入一个列表中。

+ +

函数可以嵌套

+ +

Python 允许在函数中定义函数,这通常被称为嵌套函数或内部函数。

+ +
def speak(text):
+    def whisper(t):
+        return t.lower()+"..."
+    return whisper(text)
+speak('Hello, World') # 'hello, world...'
+
+ +

每次调用 speak 时,都会定义一个新的内部函数 whisper 并立即调用。’Hello, World’被传递给text参数,然后whisper函数被调用,返回whisper(‘Hello, World’) = ‘hello, world…‘,最后speak函数返回这个值。

+ +

内部函数可以访问外部函数的局部变量,但外部函数不能访问内部函数的局部变量。也就是说whisper函数可以访问speak函数的text参数,但speak函数不能访问whisper函数的t参数。

+ +
whisper('Hello, World') # NameError: name 'whisper' is not defined
+
+ +

whisper 函数的作用域仅限于 speak 函数,因此在 speak 函数外部无法访问它。

+ +

那怎么才能从 speak 外部访问嵌套的 whisper 函数呢?由于函数是对象,因此可以将内部函数返回给父函数的调用者.

+ +

例如,下面这个函数定义了两个内部函数。顶层函数根据传递进来的参数向调用者返回对应的内部函数:

+ +
def get_speak_func(text, volume):
+    def whisper():
+        return text.lower()+"..."
+    def yell():
+        return text.upper()+"!"
+    if volume > 0.5:
+        return yell
+    else:
+        return whisper
+get_speak_func('Hello, World', 0.7) # <function get_speak_func.<locals>.yell at 0x7f9b8c0b0d08>
+get_speak_func('Hello, world',0.3) # <function get_speak_func.<locals>.whisper at 0x7f9b8c0b0c80>
+
+ +

也就是说,get_speak_func 实际上不调用任何内部函数,只是根据 volume 参数选择适当的内部函数,然后返回这个函数对象。

+ +

如何调用这个函数对象呢?可以像调用普通函数一样调用它:

+ +
speak_func = get_speak_func('Hello, World', 0.7)
+speak_func('Hello') # 'HELLO!'
+
+ +

这意味着函数不仅可以通过参数接受行为,还可以返回行为。这里的 speak_func 函数就是一个行为。

+ +

函数可捕捉局部状态

+ +

内部函数不仅可以从父函数返回,还可以捕获并携带父函数的某些状态。

+ +

下面对前面的 get_speak_func 示例做些小改动来逐步说明这一点。新版在内部就会使用 +volume 和 text 参数,因此返回的函数是可以直接调用的:

+ +
def get_speak_func(text, volume):
+    def whisper():
+        return text.lower()+"..."
+    def yell():
+        return text.upper()+"!"
+    if volume > 0.5:
+        return yell()
+    else:
+        return whisper()
+
+
+

仔细看看内部函数 whisper 和 yell,注意其中并没有 text 参数。但不知何故,内部函数仍然可以访问在父函数中定义的 text 参数。它们似乎捕捉并“记住”了这个参数的值。 +拥有这种行为的函数被称为词法闭包(lexical closure),简称闭包。闭包在程序流不在闭包范围内的情况下,也能记住封闭作用域(enclosing scope)中的值.

+ +

词法闭包的一个重要特性是,它们可以捕捉并携带封闭作用域中的状态。这意味着函数不仅可以返回行为,还可以预先配置这些行为。

+ +
def make_adder(n): 
+    def add(x):
+        return x + n
+    return add # 返回函数对象
+plus_3 = make_adder(3) # plus_3 是一个add函数
+plus_3(4) # 7
+plus_5 = make_adder(5) # plus_5 是一个add函数,但是n的值不同
+plus_5(4) # 9
+
+

在这个例子中,make_adder 作为工厂函数来创建和配置各种 adder 函数。注意,这些 adder函数仍然可以访问 make_adder 函数中位于封闭作用域中的参数n。

+ +

对象也可以作为函数使用

+ +

虽然 Python 中的所有函数都是对象,但反之不成立。有些对象不是函数,但依然可以调用,因此在许多情况下可以将其当作函数来对待。

+ +

如果一个对象是可调用的,意味着可以使用圆括号函数调用语法,甚至可以传入调用参数。 +这些都由__call__双下划线方法完成。

+ +
class Adder:
+    def __init__(self, n):
+        self.n = n
+    def __call__(self, x):
+        return self.n + x # self.n 是Adder对象的属性,x是调用时传入的参数
+plus_3 = Adder(3) # plus_3 是一个Adder对象,调用__init__方法
+# 在幕后,像函数那样“调用”一个对象实例实际上是在尝试执行该对象的__call__方法
+plus_3(4) # 7,调用__call__方法,返回self.n + x
+
+ +

在这个例子中,Adder 类的实例可以像函数一样被调用。这是因为 Adder 类定义了__call__方法,这个方法的实现就是一个函数。当然,并不是所有的对象都可以调用,因此 Python 内置了 callable 函数,用于检查一个对象是否可以调用。

+ +

lambda 是单表达式函数

+ +

你可能想知道 lambda 有什么独特之处:如果只是比用 def 声明函数稍微方便一点,那有什么大不了的?

+ +

来看下面的例子,同时脑海里要记着函数表达式这个概念:

+ +
(lamba x,y:x+y)(5,3)# 8,lambda表达式的结果是一个函数对象,意味着可以使用圆括号函数调用语法,圆括号之后的参数是传入函数的参数
+
+ +

从概念上讲,lambda 表达式 lambda x,y:x + y 与用 def 声明函数相同,但从语法上来说表达式位于 lambda 内部。两者的关键区别在于,lambda 不必先将函数对象与名称绑定,只需在 lambda 中创建一个想要执行的表达式,然后像普通函数那样立即调用进行计算。

+ +

lambda 和普通函数定义之间还有另一个语法差异。lambda 函数只能含有一个表达式,这意味着 lambda 函数不能使用语句或注解(annotation),甚至不能使用返回语句。 +那么应该如何从 lambda 返回值呢?执行 lambda 函数时会计算其中的表达式,然后自动返回表达式的结果,所以其中总是有一个隐式的返回表达式。因此有些人把 lambda 称为单表达式函数

+ +

lambda 的用途

+ +

从技术上讲,每当需要提供一个函数对象时,就可以使用 lambda 表达式。而且,因为 lambda 是匿名的,所以不需要先分配一个名字.

+ +

比如说,可以用lamba表达式来定义简短的key函数:

+ +
tuples = [(1,'d'),(2,'b'),(3,'c'),(4,'a')]
+sorted(tuples,key = lamba x: x[1]) # [(4,'a'),(2,'b'),(3,'c'),(1,'d')]
+
+ +

上面的例子按照每个元组中的第 2 个值对元组列表进行排序。在这种情况下,用 lambda 函数能快速修改排序顺序。

+ +

下面是另外一个例子,使用lambda函数快速改变排序顺序:

+ +
sorted(range(-5,6),lamba x: x*x) # [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]
+
+ +

lamba函数的另外应用场景是作为回调函数传递给其他函数。比如,下面的代码使用 lambda 函数来定义一个回调函数,然后将其传递给内置的 filter 函数:

+ +
list(filter(lambda x: x % 2, range(10))) # [1, 3, 5, 7, 9]
+# filter函数的第一个参数是一个函数对象,第二个参数是一个可迭代对象
+
+

lambda 还有一个有趣之处:与普通的嵌套函数一样,lambda 也可以像词法闭包那样工作。

+ +

+def make_repeater(n):
+    return lambda s: s * n
+repeat_5 = make_repeater(5)
+repeat_5(3) # 15
+repeat_5('hello') # 'hellohellohellohellohello'
+repeat_3 = make_repeater(3)
+repeat_3('hello') # 'hellohellohello'
+repeat_3(3) # 9
+
+ +

lambda 的局限性

+ +

若工作代码用到了 lambda,虽然看起来很“酷”,但实际上对自己和同事都是一种负担。

+ +

将 lambda 和 map()或 filter()结合起来构建复杂的表达式也很难让人理解,此时用列表解析式或生成器表达式通常会清晰不少.

+ +
# 用lambda和filter构建复杂的表达式
+list(filter(lambda x: x % 2==0, range(16))) # [0, 2, 4, 6, 8, 10, 12, 14]
+# 用列表解析式
+[x for x in range(16) if x % 2 == 0] # [0, 2, 4, 6, 8, 10, 12, 14]
+
+ +

装饰器

+ +

Python 的装饰器可以用来临时扩展和修改可调用对象(函数、方法和类)的行为,同时又不会永久修改可调用对象本身。

+ +

装饰器的一大用途是将通用的功能应用到现有的类或函数的行为上,这些功能包括:

+ + + +

装饰器是用来“装饰”或“包装”另一个函数的,在被包装函数运行之前和之后执行一些代码。

+ +

那么简单装饰器的实现会是什么样子的呢?用基本术语来说,装饰器是可调用的,将可调用 +对象作为输入并返回另一个可调用对象。

+ +

下面这个函数就具有这种特性,因此可以认为它是最简单的装饰器:

+ +
def null_decorator(func):
+    return func
+
+ +

下面用这个装饰器函数装饰(或包装)另一个函数:

+ +
def greet():
+    return 'Hello!'
+greet = null_decorator(greet)
+greet() # 'Hello!'
+
+ +

这个例子中定义了一个 greet 函数,然后立即运行 null_decorator 函数来装饰它。这个例子看起来没什么用,因为 null_decorator 是刻意设计的空装饰器。

+ +

Python中有一个语法糖,可以让装饰器更加简洁,就是使用 @ 符号。下面的代码和上面的代码是等价的:

+ +
@null_decorator
+def greet():
+    return 'Hello!'
+greet() # 'Hello!'
+
+ +

在函数定义之前放置一个@null_decorator,相当于先定义函数然后运行这个装饰器。

+ +

注意,使用@语法会在定义时就立即修饰该函数。这样,若想访问未装饰的原函数则需要折 +腾一番。因此如果想保留调用未装饰函数的能力,那么还是要手动装饰需要处理的函数。

+ +

装饰器可以修改行为

+ +

在熟悉装饰器语法之后,下面来编写一个有实际作用的装饰器来修改被装饰函数的行为。

+ +

这个装饰器将被装饰函数返回的结果转换成大写字母:

+ +
def uppercase(func):
+    def wrapper():
+        original_result = func() 
+        modified_result = original_result.upper() # 修改行为, 转换成大写
+        return modified_result # 返回修改后的结果
+    return wrapper
+
+ +

这个装饰器的实现中,定义了一个内部函数 wrapper,它将被装饰函数的返回值转换成大写字母。然后,这个内部函数被返回,作为装饰器函数。

+ +

这个 uppercase 装饰器不像之前那样直接返回输入函数,而是在其中定义一个新函数(闭 +包)。在调用原函数时,新函数会包装原函数来修改其行为。

+ +

包装闭包(新函数)可以访问未经装饰的输入函数(原函数),并且可在调用输入函数之前 +和之后自由执行额外的代码。

+ +

来看看加了uppercase装饰器的greet函数,会对原来的greet函数产生什么影响:

+
@uppercase
+def greet():
+    return 'Hello!'
+greet() # 'HELLO!'
+
+ +

与 null_decorator 不同,uppercase 装饰器在装饰函数时会返回一个不同的函数对象:

+ +
>>> greet 
+<function greet at 0x10e9f0950> 
+>>> null_decorator(greet) 
+<function greet at 0x10e9f0950> 
+>>> uppercase(greet) 
+<function uppercase.<locals>.wrapper at 0x76da02f28> 
+
+ +

uppercase 定义并返回了另一个函数(闭包),这个函数在后续调用时会运行原输入函数并修改其结果.

+ +

利用这种特性可以将可重用的代码块(如日志记录和其他功能)应用于现有的函数和类。因 +此装饰器是 Python 中非常强大的功能,在标准库和第三方包中经常用到.

+ +

函数式编程

+ +

函数式编程通过在函数中定义表达式和对表达式求值完成计算。它尽量避免由于状态变化和使用可变对象引入复杂性,让程序变得简洁明了。

+ +

编程范式并没有统一的划分标准。其中两个范式:函数式编程和命令式编程。

+ +

在命令式语言(比如 Python)中,计算的状态是通过不同命名空间中变量的值反映的。变量的值决定计算的当前状态,一条语句通过增加或改变(甚至是删除)变量来改变当前状态。“命令式”语言的每一条语句都是一个通过某种方式改变状态的命令。

+ +

有两种方法可以返回一系列值,而不是生成器表达式。

+ + + +

来源

+ +

《Python函数式编程第2版》没看懂

+ +

《深入理解Python特性》28-40页

+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/54/c7243f767b62659de9a594cc3f777fa5ed502b826d214927d3c6c3cae5e2eb b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/54/c7243f767b62659de9a594cc3f777fa5ed502b826d214927d3c6c3cae5e2eb new file mode 100644 index 0000000000..b076409ab9 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/54/c7243f767b62659de9a594cc3f777fa5ed502b826d214927d3c6c3cae5e2eb @@ -0,0 +1,11 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/56/64a27f7a5b09cea1f19bc9ba91a59c1bced58360b93a97c6e4c71b765ac33a b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/56/64a27f7a5b09cea1f19bc9ba91a59c1bced58360b93a97c6e4c71b765ac33a new file mode 100644 index 0000000000..546d4daa17 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/56/64a27f7a5b09cea1f19bc9ba91a59c1bced58360b93a97c6e4c71b765ac33a @@ -0,0 +1,9 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/58/405baca6d4424a4a780e588995457ae494990f38a08ec67e9471383a0adbe5 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/58/405baca6d4424a4a780e588995457ae494990f38a08ec67e9471383a0adbe5 new file mode 100644 index 0000000000..51040941db --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/58/405baca6d4424a4a780e588995457ae494990f38a08ec67e9471383a0adbe5 @@ -0,0 +1,2 @@ +I"O

回顾一下昨天有关引用传递与值传递有关的内容。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5a/18773190c8af55b5ede0dd0371fb72f1ec0b5b720e6b9992a17142c94f9aa2 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5a/18773190c8af55b5ede0dd0371fb72f1ec0b5b720e6b9992a17142c94f9aa2 new file mode 100644 index 0000000000..492a9503dd --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5a/18773190c8af55b5ede0dd0371fb72f1ec0b5b720e6b9992a17142c94f9aa2 @@ -0,0 +1,22 @@ +I"

在VScode中如何配置

+ +
    +
  1. 下载扩展“Live Server”,可以实时刷新和浏览在浏览器中显示的内容 +live server
  2. +
  3. 记住一些快捷键,比如说使用(!+Enter)可以补全基本的HTML文件信息,比如说(.+ClassName)可以创建一个标签属性为ClassName的块级元素。 +!+Enter +.+ClassName
  4. +
+ +

简介

+ +

HTML、CSS和JavaScript的关系如下:

+ +

HTML是网页的结构,CSS是网页的外观,而JavaScript是页面的行为

+ +

HTML基本语法

+ +

CSS基本语法

+ +

Javascript

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5a/63ef031c654d1b86cf4d25b4d96787e53c37d4abd442c1aed75c17fe7aca45 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5a/63ef031c654d1b86cf4d25b4d96787e53c37d4abd442c1aed75c17fe7aca45 new file mode 100644 index 0000000000..04656308d5 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5a/63ef031c654d1b86cf4d25b4d96787e53c37d4abd442c1aed75c17fe7aca45 @@ -0,0 +1,40 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5b/35427906cd1dc545dc0f39a33111144ca53777f7680d0d1051f6a4cc98e7b9 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5b/35427906cd1dc545dc0f39a33111144ca53777f7680d0d1051f6a4cc98e7b9 new file mode 100644 index 0000000000..c7a6f11cc8 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5b/35427906cd1dc545dc0f39a33111144ca53777f7680d0d1051f6a4cc98e7b9 @@ -0,0 +1,10 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5b/9c0b64a1321a0a12b8bfb35035f4c2c2e3fe6428af995e361cc5a8208b27b0 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5b/9c0b64a1321a0a12b8bfb35035f4c2c2e3fe6428af995e361cc5a8208b27b0 new file mode 100644 index 0000000000..21b2497b19 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5b/9c0b64a1321a0a12b8bfb35035f4c2c2e3fe6428af995e361cc5a8208b27b0 @@ -0,0 +1,32 @@ +I"

介绍最近发现的实用小工具: 在线查看github仓库的项目结构,pycharm中好用的插件.

+ +

在线查看github仓库的项目结构

+ +

方式一:使用gitpod

+ +

Gitpod是一个在线的IDE,可以直接在浏览器中打开github仓库,查看项目结构,编辑代码,运行代码,提交代码等等。

+
https://github.com/JuliaGeo/NaturalEarth.jl
+
+ +

添加前缀gitpod.io/#/:

+
https://gitpod.io/#/github.dev/JuliaGeo/NaturalEarth.jl
+
+ +

方式二:按下句号

+ +

按下句号,可以在github仓库中查看项目结构,但是只能查看,不能编辑代码。 +举例子:

+
https://github.com/JuliaGeo/NaturalEarth.jl
+
+ +

按下句号之后,会进入下面的页面:

+
https://github.dev/JuliaGeo/NaturalEarth.jl
+
+ +

这是一个在线的IDE,可以直接在浏览器中打开github仓库,查看项目结构,等等。

+ +

pycharm中好用的插件

+ +

Key Promoter X +Key Promoter X 是用于基于 IntelliJ 产品(如 IDEA,Android Studio 或 CLion)的插件,它有助于在工作时从鼠标操作中 学习基本的键盘快捷键。 当您在 IDE 内部的按钮上 使用鼠标时,Key Promoter X 会显示您应该使用的键盘快捷键(如下图)。 这提供了一种简单的方法来学习如何用键盘按键代替繁琐的鼠标工作,并有助于过渡到更快的 无鼠标开发。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5b/d60e13dfa915a87b29fabe3a2d3338db338de81055fa8f1d4135f4debe564f b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5b/d60e13dfa915a87b29fabe3a2d3338db338de81055fa8f1d4135f4debe564f new file mode 100644 index 0000000000..8683ef09df --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5b/d60e13dfa915a87b29fabe3a2d3338db338de81055fa8f1d4135f4debe564f @@ -0,0 +1,8 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5c/960fe41a55ff0957c57d4ea2645131080b19e04e34f87a877fd8769183b833 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5c/960fe41a55ff0957c57d4ea2645131080b19e04e34f87a877fd8769183b833 new file mode 100644 index 0000000000..3741f08cd3 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5c/960fe41a55ff0957c57d4ea2645131080b19e04e34f87a877fd8769183b833 @@ -0,0 +1,12 @@ +I"x + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5f/ed42ba27b808419a04b0720479e036f9306848d640a4c13c25f022e3457d4a b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5f/ed42ba27b808419a04b0720479e036f9306848d640a4c13c25f022e3457d4a new file mode 100644 index 0000000000..1ecd6db9be --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/5f/ed42ba27b808419a04b0720479e036f9306848d640a4c13c25f022e3457d4a @@ -0,0 +1,2 @@ +I"

wind turbine数据,如何使用机器学习算法预测轴承健康程度和风功率曲线呢

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/63/cdcc2d98977179ae7faf773f13ad74111c1e3419c9b1f7d3f340c335bc5bfd b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/63/cdcc2d98977179ae7faf773f13ad74111c1e3419c9b1f7d3f340c335bc5bfd new file mode 100644 index 0000000000..04ea52b20e --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/63/cdcc2d98977179ae7faf773f13ad74111c1e3419c9b1f7d3f340c335bc5bfd @@ -0,0 +1,28 @@ +I"h + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/64/7c732ecb4de6aaf6f9f0dd456f97ec2493b692fc8a80676a4b8fc37b97e432 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/64/7c732ecb4de6aaf6f9f0dd456f97ec2493b692fc8a80676a4b8fc37b97e432 new file mode 100644 index 0000000000..a09de8467a --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/64/7c732ecb4de6aaf6f9f0dd456f97ec2493b692fc8a80676a4b8fc37b97e432 @@ -0,0 +1,382 @@ +I";

对CPU进行压力测试

+ +

stress.sh

+ +
#对CPU进行压力测试
+cpu_number=$(grep -c 'processor' /proc/cpuinfo)
+#模拟CPU在内核使用率达到100%的场景。
+stress --cpu $cpu_number
+
+

输出结果导txt文件中(StressResult.sh)

+ +
#!/bin/bash
+# 添加当前时间信息
+echo "当前时间为$(date)" >> stress.txt
+echo "模拟CPU在内核使用率达到100%的场景:" >> stress.txt
+
+./stress.sh & # run stress.sh in the background
+pid=$! # get the PID of the background process
+
+SECONDS=0 # initialize SECONDS variable to 0
+
+while [ $SECONDS -lt 50 ] # check if script has been running for less than 50 seconds
+do
+  echo "Current value of SECONDS: $SECONDS" >> stress.txt
+  uptime >> stress.txt
+  #echo "\n处理器实时统计信息如下" >> stress.txt
+  #-P ALL参数指定该命令应报告所有处理器的统计信息
+  #mpstat -P ALL 5 1 >> stress.txt
+  #echo "\n" >> stress.txt
+  SECONDS=$((SECONDS+2))
+  sleep 2
+done
+
+kill -9 $pid # kill the background process after 50 seconds
+kill $(pgrep stress)
+kill $(pgrep StressResult.sh)
+kill $(pgrep sleep)
+
+ +

结果

+ +
当前时间为2023年 03月 29日 星期三 14:34:33 CST
+模拟CPU在内核使用率达到100%的场景:
+Current value of SECONDS: 0
+ 14:34:33 up 59 min,  1 user,  load average: 2.49, 2.49, 2.14
+Current value of SECONDS: 2
+ 14:34:35 up 59 min,  1 user,  load average: 2.61, 2.52, 2.15
+Current value of SECONDS: 4
+ 14:34:38 up 59 min,  1 user,  load average: 2.61, 2.52, 2.15
+Current value of SECONDS: 6
+ 14:34:40 up 59 min,  1 user,  load average: 2.61, 2.52, 2.15
+Current value of SECONDS: 8
+ 14:34:42 up 59 min,  1 user,  load average: 2.80, 2.56, 2.16
+Current value of SECONDS: 10
+ 14:34:44 up 59 min,  1 user,  load average: 2.80, 2.56, 2.16
+Current value of SECONDS: 12
+ 14:34:46 up 59 min,  1 user,  load average: 2.98, 2.60, 2.18
+Current value of SECONDS: 14
+ 14:34:48 up 59 min,  1 user,  load average: 2.98, 2.60, 2.18
+Current value of SECONDS: 16
+ 14:34:50 up 59 min,  1 user,  load average: 2.98, 2.60, 2.18
+Current value of SECONDS: 18
+ 14:34:52 up 59 min,  1 user,  load average: 3.06, 2.62, 2.19
+Current value of SECONDS: 20
+ 14:34:54 up 59 min,  1 user,  load average: 3.06, 2.62, 2.19
+Current value of SECONDS: 22
+ 14:34:56 up 59 min,  1 user,  load average: 3.29, 2.68, 2.21
+Current value of SECONDS: 24
+ 14:34:58 up 59 min,  1 user,  load average: 3.29, 2.68, 2.21
+Current value of SECONDS: 26
+ 14:35:00 up 59 min,  1 user,  load average: 3.29, 2.68, 2.21
+Current value of SECONDS: 28
+ 14:35:02 up 59 min,  1 user,  load average: 3.43, 2.72, 2.22
+Current value of SECONDS: 30
+ 14:35:04 up 59 min,  1 user,  load average: 3.43, 2.72, 2.22
+Current value of SECONDS: 32
+ 14:35:06 up 59 min,  1 user,  load average: 3.48, 2.74, 2.23
+Current value of SECONDS: 34
+ 14:35:08 up 59 min,  1 user,  load average: 3.48, 2.74, 2.23
+Current value of SECONDS: 36
+ 14:35:10 up 59 min,  1 user,  load average: 3.52, 2.76, 2.24
+Current value of SECONDS: 38
+ 14:35:12 up  1:00,  1 user,  load average: 3.52, 2.76, 2.24
+Current value of SECONDS: 40
+ 14:35:14 up  1:00,  1 user,  load average: 3.52, 2.76, 2.24
+Current value of SECONDS: 42
+ 14:35:16 up  1:00,  1 user,  load average: 3.72, 2.82, 2.26
+Current value of SECONDS: 44
+ 14:35:18 up  1:00,  1 user,  load average: 3.72, 2.82, 2.26
+Current value of SECONDS: 46
+ 14:35:20 up  1:00,  1 user,  load average: 3.74, 2.84, 2.27
+Current value of SECONDS: 48
+ 14:35:22 up  1:00,  1 user,  load average: 3.74, 2.84, 2.27
+
+ +

对内存进行压力测试

+ +

memory.sh

+ +
# 新增4个内存分配进程,每次分配大小730M,分配后不释放,长期保持测试 
+stress --vm 4 --vm-bytes 730M --vm-keep
+
+ +

输出结果导txt文件中(memoryResult.sh)

+ +
#!/bin/bash
+# 添加当前时间信息
+echo "当前时间为$(date)" >> memorystress.txt
+echo "# 新增4个内存分配进程,每次分配大小730M,分配后不释放,长期保持测试 " >> memorystress.txt
+
+./memory.sh & # run stress.sh in the background
+pid=$! # get the PID of the background process
+
+SECONDS=0 # initialize SECONDS variable to 0
+
+while [ $SECONDS -lt 50 ] # check if script has been running for less than 50 seconds
+do
+  echo "Current value of SECONDS: $SECONDS" >> memorystress.txt
+  uptime >> memorystress.txt
+  #echo "\n处理器实时统计信息如下" >> stress.txt
+  #-P ALL参数指定该命令应报告所有处理器的统计信息
+  #mpstat -P ALL 5 1 >> stress.txt
+  #echo "\n" >> stress.txt
+  SECONDS=$((SECONDS+2))
+  sleep 2
+done
+
+kill -9 $pid # kill the background process after 50 seconds
+kill $(pgrep stress)
+kill $(pgrep memoryResult.sh)
+kill $(pgrep sleep)
+
+ +

执行memoryResult.sh

+ +
[root@localhost ~]# ./memoryResult.sh
+
+ +

输出结果

+ +
当前时间为2023年 03月 29日 星期三 14:32:07 CST
+# 新增4个内存分配进程,每次分配大小730M,分配后不释放,长期保持测试 
+Current value of SECONDS: 0
+ 14:32:07 up 56 min,  1 user,  load average: 2.24, 2.34, 2.02
+Current value of SECONDS: 2
+ 14:32:09 up 56 min,  1 user,  load average: 2.24, 2.34, 2.02
+Current value of SECONDS: 4
+ 14:32:11 up 57 min,  1 user,  load average: 2.46, 2.38, 2.04
+Current value of SECONDS: 6
+ 14:32:13 up 57 min,  1 user,  load average: 2.46, 2.38, 2.04
+Current value of SECONDS: 8
+ 14:32:15 up 57 min,  1 user,  load average: 2.46, 2.38, 2.04
+Current value of SECONDS: 10
+ 14:32:17 up 57 min,  1 user,  load average: 2.83, 2.46, 2.07
+Current value of SECONDS: 12
+ 14:32:19 up 57 min,  1 user,  load average: 2.83, 2.46, 2.07
+Current value of SECONDS: 14
+ 14:32:21 up 57 min,  1 user,  load average: 2.92, 2.48, 2.08
+Current value of SECONDS: 16
+ 14:32:23 up 57 min,  1 user,  load average: 2.92, 2.48, 2.08
+Current value of SECONDS: 18
+ 14:32:25 up 57 min,  1 user,  load average: 3.01, 2.51, 2.09
+Current value of SECONDS: 20
+ 14:32:27 up 57 min,  1 user,  load average: 3.01, 2.51, 2.09
+Current value of SECONDS: 22
+ 14:32:29 up 57 min,  1 user,  load average: 3.01, 2.51, 2.09
+Current value of SECONDS: 24
+ 14:32:31 up 57 min,  1 user,  load average: 3.09, 2.53, 2.10
+Current value of SECONDS: 26
+ 14:32:33 up 57 min,  1 user,  load average: 3.09, 2.53, 2.10
+Current value of SECONDS: 28
+ 14:32:35 up 57 min,  1 user,  load average: 3.16, 2.56, 2.11
+Current value of SECONDS: 30
+ 14:32:37 up 57 min,  1 user,  load average: 3.16, 2.56, 2.11
+Current value of SECONDS: 32
+ 14:32:39 up 57 min,  1 user,  load average: 3.16, 2.56, 2.11
+Current value of SECONDS: 34
+ 14:32:41 up 57 min,  1 user,  load average: 3.23, 2.58, 2.12
+Current value of SECONDS: 36
+ 14:32:43 up 57 min,  1 user,  load average: 3.23, 2.58, 2.12
+Current value of SECONDS: 38
+ 14:32:45 up 57 min,  1 user,  load average: 3.29, 2.61, 2.13
+Current value of SECONDS: 40
+ 14:32:47 up 57 min,  1 user,  load average: 3.29, 2.61, 2.13
+Current value of SECONDS: 42
+ 14:32:49 up 57 min,  1 user,  load average: 3.29, 2.61, 2.13
+Current value of SECONDS: 44
+ 14:32:51 up 57 min,  1 user,  load average: 3.35, 2.63, 2.14
+Current value of SECONDS: 46
+ 14:32:53 up 57 min,  1 user,  load average: 3.35, 2.63, 2.14
+Current value of SECONDS: 48
+ 14:32:55 up 57 min,  1 user,  load average: 3.40, 2.65, 2.15
+
+ +

对IO进行压力测试

+ +

io.sh

+ +
# 新增6个I/O进程,测试100秒
+stress -i 6
+
+ +

ioResult.sh(输出结果到iostress.txt)

+ +
#!/bin/bash
+# 添加当前时间信息
+echo "当前时间为$(date)" >> iostress.txt
+echo "模拟新增6个I/O进程的场景:" >> iostress.txt
+
+./io.sh & # run stress.sh in the background
+pid=$! # get the PID of the background process
+
+SECONDS=0 # initialize SECONDS variable to 0
+
+while [ $SECONDS -lt 50 ] # check if script has been running for less than 50 seconds
+do
+  echo "Current value of SECONDS: $SECONDS" >> iostress.txt
+  uptime >> iostress.txt
+  #echo "\n处理器实时统计信息如下" >> stress.txt
+  #-P ALL参数指定该命令应报告所有处理器的统计信息
+  #mpstat -P ALL 5 1 >> stress.txt
+  #echo "\n" >> stress.txt
+  SECONDS=$((SECONDS+2))
+  sleep 2
+done
+
+kill -9 $pid # kill the background process after 50 seconds
+kill $(pgrep stress)
+kill $(pgrep ioResult.sh)
+kill $(pgrep sleep)
+
+ +

查看结果

+ +
当前时间为2023年 03月 29日 星期三 14:30:31 CST
+模拟新增6个I/O进程的场景:
+Current value of SECONDS: 0
+ 14:30:31 up 55 min,  1 user,  load average: 1.69, 1.90, 1.84
+Current value of SECONDS: 2
+ 14:30:33 up 55 min,  1 user,  load average: 1.69, 1.90, 1.84
+Current value of SECONDS: 4
+ 14:30:35 up 55 min,  1 user,  load average: 1.69, 1.90, 1.84
+Current value of SECONDS: 6
+ 14:30:37 up 55 min,  1 user,  load average: 2.04, 1.97, 1.87
+Current value of SECONDS: 8
+ 14:30:39 up 55 min,  1 user,  load average: 2.04, 1.97, 1.87
+Current value of SECONDS: 10
+ 14:30:41 up 55 min,  1 user,  load average: 2.44, 2.05, 1.89
+Current value of SECONDS: 12
+ 14:30:43 up 55 min,  1 user,  load average: 2.44, 2.05, 1.89
+Current value of SECONDS: 14
+ 14:30:45 up 55 min,  1 user,  load average: 2.44, 2.05, 1.89
+Current value of SECONDS: 16
+ 14:30:47 up 55 min,  1 user,  load average: 3.04, 2.19, 1.94
+Current value of SECONDS: 18
+ 14:30:49 up 55 min,  1 user,  load average: 3.04, 2.19, 1.94
+Current value of SECONDS: 20
+ 14:30:51 up 55 min,  1 user,  load average: 3.60, 2.32, 1.98
+Current value of SECONDS: 22
+ 14:30:53 up 55 min,  1 user,  load average: 3.60, 2.32, 1.98
+Current value of SECONDS: 24
+ 14:30:55 up 55 min,  1 user,  load average: 3.60, 2.32, 1.98
+Current value of SECONDS: 26
+ 14:30:57 up 55 min,  1 user,  load average: 3.95, 2.41, 2.01
+Current value of SECONDS: 28
+ 14:30:59 up 55 min,  1 user,  load average: 3.95, 2.41, 2.01
+Current value of SECONDS: 30
+ 14:31:01 up 55 min,  1 user,  load average: 4.20, 2.49, 2.04
+Current value of SECONDS: 32
+ 14:31:03 up 55 min,  1 user,  load average: 4.20, 2.49, 2.04
+Current value of SECONDS: 34
+ 14:31:05 up 55 min,  1 user,  load average: 4.26, 2.53, 2.06
+Current value of SECONDS: 36
+ 14:31:07 up 55 min,  1 user,  load average: 4.26, 2.53, 2.06
+Current value of SECONDS: 38
+ 14:31:09 up 55 min,  1 user,  load average: 4.26, 2.53, 2.06
+Current value of SECONDS: 40
+ 14:31:11 up 56 min,  1 user,  load average: 4.40, 2.59, 2.08
+Current value of SECONDS: 42
+ 14:31:13 up 56 min,  1 user,  load average: 4.40, 2.59, 2.08
+Current value of SECONDS: 44
+ 14:31:15 up 56 min,  1 user,  load average: 4.53, 2.64, 2.10
+Current value of SECONDS: 46
+ 14:31:17 up 56 min,  1 user,  load average: 4.53, 2.64, 2.10
+Current value of SECONDS: 48
+ 14:31:19 up 56 min,  1 user,  load average: 4.53, 2.64, 2.10
+
+ +

对磁盘IO进行压力测试

+ +

hdd.sh

+ +
# 开启2个磁盘IO进程,每次写10GB数据到磁盘
+stress --hdd 2 --hdd-bytes 10G --backoff 2000000
+
+ +

hddResult.sh(输出结果到hddstress.txt)

+ +
#!/bin/bash
+# 添加当前时间信息
+echo "当前时间为$(date)" >> hddstress.txt
+echo "模拟开启2个磁盘IO进程,每次写10GB数据到磁盘的场景:" >> hddstress.txt
+
+./hdd.sh & # run stress.sh in the background
+pid=$! # get the PID of the background process
+
+SECONDS=0 # initialize SECONDS variable to 0
+
+while [ $SECONDS -lt 50 ] # check if script has been running for less than 50 seconds
+do
+  echo "Current value of SECONDS: $SECONDS" >> hddstress.txt
+  uptime >> hddstress.txt
+  #echo "\n处理器实时统计信息如下" >> stress.txt
+  #-P ALL参数指定该命令应报告所有处理器的统计信息
+  #mpstat -P ALL 5 1 >> stress.txt
+  #echo "\n" >> stress.txt
+  SECONDS=$((SECONDS+2))
+  sleep 2
+done
+
+kill -9 $pid # kill the background process after 50 seconds
+kill $(pgrep stress)
+kill $(pgrep hddResult.sh)
+kill $(pgrep sleep)
+
+ +

查看结果

+ +
当前时间为2023年 03月 29日 星期三 14:33:22 CST
+模拟开启2个磁盘IO进程,每次写10GB数据到磁盘的场景:
+Current value of SECONDS: 0
+ 14:33:22 up 58 min,  1 user,  load average: 2.24, 2.44, 2.09
+Current value of SECONDS: 2
+ 14:33:24 up 58 min,  1 user,  load average: 2.24, 2.44, 2.09
+Current value of SECONDS: 4
+ 14:33:26 up 58 min,  1 user,  load average: 2.22, 2.43, 2.09
+Current value of SECONDS: 6
+ 14:33:28 up 58 min,  1 user,  load average: 2.22, 2.43, 2.09
+Current value of SECONDS: 8
+ 14:33:30 up 58 min,  1 user,  load average: 2.22, 2.43, 2.09
+Current value of SECONDS: 10
+ 14:33:32 up 58 min,  1 user,  load average: 2.28, 2.44, 2.10
+Current value of SECONDS: 12
+ 14:33:34 up 58 min,  1 user,  load average: 2.28, 2.44, 2.10
+Current value of SECONDS: 14
+ 14:33:36 up 58 min,  1 user,  load average: 2.34, 2.45, 2.10
+Current value of SECONDS: 16
+ 14:33:38 up 58 min,  1 user,  load average: 2.34, 2.45, 2.10
+Current value of SECONDS: 18
+ 14:33:40 up 58 min,  1 user,  load average: 2.39, 2.46, 2.11
+Current value of SECONDS: 20
+ 14:33:42 up 58 min,  1 user,  load average: 2.39, 2.46, 2.11
+Current value of SECONDS: 22
+ 14:33:44 up 58 min,  1 user,  load average: 2.39, 2.46, 2.11
+Current value of SECONDS: 24
+ 14:33:46 up 58 min,  1 user,  load average: 2.44, 2.47, 2.11
+Current value of SECONDS: 26
+ 14:33:48 up 58 min,  1 user,  load average: 2.44, 2.47, 2.11
+Current value of SECONDS: 28
+ 14:33:51 up 58 min,  1 user,  load average: 2.49, 2.48, 2.12
+Current value of SECONDS: 30
+ 14:33:53 up 58 min,  1 user,  load average: 2.49, 2.48, 2.12
+Current value of SECONDS: 32
+ 14:33:55 up 58 min,  1 user,  load average: 2.53, 2.49, 2.12
+Current value of SECONDS: 34
+ 14:33:58 up 58 min,  1 user,  load average: 2.53, 2.49, 2.12
+Current value of SECONDS: 36
+ 14:34:01 up 58 min,  1 user,  load average: 2.49, 2.48, 2.12
+Current value of SECONDS: 38
+ 14:34:03 up 58 min,  1 user,  load average: 2.49, 2.48, 2.12
+Current value of SECONDS: 40
+ 14:34:04 up 58 min,  1 user,  load average: 2.49, 2.48, 2.12
+Current value of SECONDS: 42
+ 14:34:09 up 58 min,  1 user,  load average: 2.45, 2.47, 2.12
+Current value of SECONDS: 44
+ 14:34:12 up 59 min,  1 user,  load average: 2.49, 2.48, 2.12
+Current value of SECONDS: 46
+ 14:34:17 up 59 min,  1 user,  load average: 2.93, 2.57, 2.15
+Current value of SECONDS: 48
+ 14:34:19 up 59 min,  1 user,  load average: 2.93, 2.57, 2.15
+
+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/64/d3d7d50ab58f0bee7021a8f7a795be89a84d154ab2ec743cdd0a86189f13ea b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/64/d3d7d50ab58f0bee7021a8f7a795be89a84d154ab2ec743cdd0a86189f13ea new file mode 100644 index 0000000000..d7840e518d --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/64/d3d7d50ab58f0bee7021a8f7a795be89a84d154ab2ec743cdd0a86189f13ea @@ -0,0 +1,144 @@ +I"

介绍了接口自动化测试框架框架搭建,第三方插件,配置文件设置和读取,测试流程等内容。测试用例设计部分,则包括Excel测试用例,控制流和数据流,方法的应用场景,测试用例书写标准以及测试集的创建。

+ +

接口自动化测试总结

+ +

框架搭建

+ + + +

主程序 -> Excel测试用例 -> Request请求 -> 断言 -> 日志 -> Allure报告 -> 邮件发送

+ +

测试设计

+ +

测试框架的设计

+ +

测试用例的设计

+ +

测试用例是为了某个特定的测试目标而设计的,它是测试操作过程序列、条件、期望结果和相关数据的一个特定的集合。

+ +

从需求出发,设计测试用例,不同的测试数据或测试条件和用户故事(user story)、应用场景叠加,形成测试用例。

+ +

还可以基于被测系统(system under test)的功能点、业务流程、业务规则、业务数据等,设计测试用例。

+ +

测试用例和功能结构有对应关系,呈现出层次结构。

+ +

例如,在“登录”功能模块中,层次结构如下:

+ + + +

Excel测试用例

+ +

一个完整的Excel测试用例表应该包括以下几个部分:

+ +

用例编号,用例名称,用例类型,用例优先级,用例状态,用例版本,用例创建人,用例创建时间,用例修改人,用例修改时间,用例描述,用例前置条件,用例后置条件,用例步骤,用例预期结果,用例附件,用例备注。

+ +

控制流和数据流

+ +

可以从代码、功能点和场景等维度去衡量测试覆盖率,但是场景、功能点具有不确定性,比较确定的测试覆盖率是基于数据分析的数据流和基于逻辑结构的控制流。测试设计方法就是通过测试用例的不断设计、优化,最终达到控制流和数据流的覆盖。

+ +

在业务层,控制流主要体现在业务流程图,数据流体现在数据流程图。

+ +

如“用户登录”功能模块的业务流程图:

+ +

"用户登录"功能模块的业务流程图

+ +

根据上述流程图来设计测试用例,需要覆盖每个分支和条件,以保证测试的覆盖率。

+ +

首先,要考虑分成“不能自动登录”和“自动登录”两类;对于“不能自动登录”,要考虑“记住密码”和“不记住密码”两种情况;对于“自动登录”,要考虑“记住密码”和“不记住密码”两种情况。对于不同输入项的错误,可以分成“用户名错误”、“密码错误”、“验证码错误”、“用户名和密码错误”、“用户名和验证码错误”、“密码和验证码错误”、“用户名、密码和验证码错误”等情况。这样,就可以设计出对呀的测试用例。

+ +

清楚方法的应用场景

+ + + +

测试用例书写标准

+ +

测试用例标准模板的主要元素如下:

+ + + +

测试集的创建

+ +

测试集是测试用例的集合,可以对测试用例进行分类管理。测试集的创建可以按照模块、功能、功能模块、功能点等方式进行分类。

+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/67/f75227eb3aa4ffa692aa5c6fc0512a0451636e9329322dbab3a930627541fd b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/67/f75227eb3aa4ffa692aa5c6fc0512a0451636e9329322dbab3a930627541fd new file mode 100644 index 0000000000..5f9b6f90fe --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/67/f75227eb3aa4ffa692aa5c6fc0512a0451636e9329322dbab3a930627541fd @@ -0,0 +1,74 @@ +I"

下划线、双下划线

+ +

单下划线和双下划线在 Python 变量名和方法名中都有各自的含义。有些仅仅是作为约定,用于提示开发人员;而另一些则对 Python 解释器有特殊含义。

+ +

前置单下划线:_var

+ +

前置单下划线只有约定含义。它对于程序员而言是一种提示。提示其他程序员,以单下划线开头的变量或方法只在内部使用。PEP8中定义了这个约定。

+ +

Python 在“私有”和“公共”变量之间并没有很强的区别。

+ +
class Test:
+    def __init(self):
+        self.foo = 11
+        self._bar = 23
+
+ +

如果实例化这个类并尝试访问在__init__构造函数中定义的 foo 和_bar 属性,会发生什么情况?

+ +
t = Test()
+print(t.foo) # 11
+print(t._bar) # 23
+
+ +

可以看到,__bar 前面的单下划线并没有阻止我们“进入”这个类访问变量的值。

+ +

后置单下划线:var_

+ +

有时,某个变量最合适的名称已被 Python 语言中的关键字占用。在这种情况下,可以追加一个下划线来绕过命名冲突。

+ +

这是PEP8中的约定。

+ +

前置双下划线:__var

+ +

双下划线前缀会让 Python 解释器重写属性名称,以避免子类中的命名冲突。

+ +

这也称为名称改写(name mangling),即解释器会更改变量的名称,以便在稍后扩展这个类时避免命名冲突。

+ +
class Test:
+    def __init__(self):
+        self.__baz = 23
+t = Test()
+print(t.__baz) # AttributeError: 'Test' object has no attribute '__baz'
+dir(t) # ['_Test__baz']
+print(t._Test__baz) # 23
+class Test2(Test):
+    def __init__(self):
+        super().__init__()# super() 函数是用于调用父类(超类)的一个方法.
+        self.__baz = 42
+t2 = Test2()
+print(t2._baz) 
+dir(t2) 
+print(t2._Test__baz) # 23
+print(t2._Test2__baz) # 42
+
+
+ +

前后双下划线:var

+ +

如果名字前后都使用双下划线,则不会发生名称改写;前后由双下划线包围的变量不受 Python 解释器的影响。

+ +

同时具有前后双下划线的名称在 Python 中有特殊用途。比如说,__init__这样的对象构造函数;用来让对象可调用的__call__函数。

+ +

这些双下划线方法通常被称为魔法方法(magic methods),因为它们的名字都是以双下划线开头和结尾的。

+ +

单下划线:_

+ +

单下划线是一个有效的变量名,用作临时或无意义变量的名称。

+ +

在 Python 交互式解释器中,单下划线是最后一个表达式的值。

+ +

来源

+ +

《深入理解Python特性》P015 - P022

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/68/1ff7b47416228d95ae9286610306df85d85f1b6d7c49243840c97cffd767b7 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/68/1ff7b47416228d95ae9286610306df85d85f1b6d7c49243840c97cffd767b7 new file mode 100644 index 0000000000..b4c5fe8e7e --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/68/1ff7b47416228d95ae9286610306df85d85f1b6d7c49243840c97cffd767b7 @@ -0,0 +1,24 @@ +I"(

软件测试是什么

+

软件测试的一个目的是验证你的猜想-猜想软件的内部逻辑,包括第三方的模块、代码包。甚至Python的内建的数据结构是如何运行的。Tasks项目使用名为Task的数据结构,它是用namedtuple工厂函数生成的。Task用于在UI层和API层之间传递信息。

+
from collections import namedtuple
+Task = namedtuple('Task', ['summary', 'owner', 'done', 'id'])
+# 使用_new_.defaults_创建一个默认的Task实例
+Task.__new__.__defaults__ = (None, None, False, None)
+
+

运行pytest时可以指定目录和文件。如果不指定,会搜索当前目录以及其子目录中以test_开头或者已_test结尾的文件。pytest会自动搜索这些文件中的测试用例。
+rootdir: D:\pytest\Tasks\tests, +inifile: pytest.ini & tox.ini & setup.cfg

+ +

测试结果 +每个文件的测试情况只占据一行,两个点号表示两个测试用例均已通过;而Failure/erro/skip/xfail/xpass表示测试用例失败/错误/跳过/预期失败/预期失败但通过,会被标记为F/E/s/x/X。

+ +

编写测试函数

+

测试函数的命名规则:test_开头或者以_test结尾。 +测试Tasks项目的文件目录如下: +Task项目文件夹

+ +

所有的测试都放在tests文件夹中,与包含程序源代码的src文件夹分开。有一些文件比较重要,如README.rst(rst是使用reStrucuredText编写的文档格式,readme是项目须知,CHANGELOG是记录项目变更的日志文件),LICENSE.txt(许可证文件),setup.py(安装脚本),setup.cfg(配置文件),pytest.ini(配置文件),tox.ini(配置文件),conftest.py(本地插件库,hook函数将自定义逻辑引入pytest,用于改善pytest的执行流程;fixtures则是一些用于测试前后执行配置及销毁逻辑的外壳函数)、init.py(初始化文件,表示函数主入口)。

+ +

使用fixture执行配置和销毁逻辑

+

@pytest.fixture()中的参数scope表示fixture的作用域,有function、class、module、session四个待选值。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/68/d9e3961ac53c8c849e350691ac5cec41e2af1220a52ca18d433cd8d105fd5e b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/68/d9e3961ac53c8c849e350691ac5cec41e2af1220a52ca18d433cd8d105fd5e new file mode 100644 index 0000000000..50f4a441dc --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/68/d9e3961ac53c8c849e350691ac5cec41e2af1220a52ca18d433cd8d105fd5e @@ -0,0 +1,2 @@ +I"

软件包管理是任何Linux发行版的重要功能之一,可简化Linux应用程序的安装和维护方法。不同的Linux发行版采用不同的方法来打包和分发软件。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/6a/2fbc251249a3a45f304812546c51bdbbcb349841605df2ff20826743272e2f b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/6a/2fbc251249a3a45f304812546c51bdbbcb349841605df2ff20826743272e2f new file mode 100644 index 0000000000..a04d6d759d --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/6a/2fbc251249a3a45f304812546c51bdbbcb349841605df2ff20826743272e2f @@ -0,0 +1,30 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/6a/b53f1efd00f90bc8f292322ffd24e52517f628ead7b317637e53c73e4ff696 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/6a/b53f1efd00f90bc8f292322ffd24e52517f628ead7b317637e53c73e4ff696 new file mode 100644 index 0000000000..3e60b1bdab --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/6a/b53f1efd00f90bc8f292322ffd24e52517f628ead7b317637e53c73e4ff696 @@ -0,0 +1,2 @@ +I"w

介绍最近发现的实用小工具: 在线查看github仓库的项目结构,pycharm中好用的插件.

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/6c/b8512fe0777b3955fcc1847f0f830dca68479c3826686d1846dd48829fda26 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/6c/b8512fe0777b3955fcc1847f0f830dca68479c3826686d1846dd48829fda26 new file mode 100644 index 0000000000..71358ee913 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/6c/b8512fe0777b3955fcc1847f0f830dca68479c3826686d1846dd48829fda26 @@ -0,0 +1,90 @@ +I"

数据介绍

+ +

哥白尼海洋服务(Copernicus Marine Service)提供免费开放的海洋数据和服务,助力海洋政策实施,支持蓝色增长和科学创新。

+ +

官网主页如下:

+ +

CMEMS

+ +

如何选择我们需要的数据

+ +

首先,我们需要注册一个账号,账号说明使用数据的用途以及我们所属的高校,大概两三天就能审核通过。

+ +

接着,是检索数据,以我们需要的sea level anomaly (SLA) 数据为例,点击OCEAN PRODUCTS,在General catalogue中输入”SLA”然后点击搜索,以下是呈现结果:

+ +

搜索SLA返回的结果

+ +

我们发现,第一个数据集(GLOBAL OCEAN GRIDDED L4 SEA SURFACE HEIGHTS AND DERIVED VARIABLES REPROCESSED (1993-ONGOING))可能是我们需要的数据,这个数据集涵括了1993年以来高度计卫星栅格海平面异常(海表面高度减去20年的平均值),同时提供了其他的一些变量如ADT(动态地形),尤其是地转流和地转异常(geostrophic currents (absolute and anomalies)),能够节省数据处理的时间,其变量时间分辨率是0.25° × 0.25°分辨率,提供数据下载格式是nc格式。

+ +

从SLA计算地转流速(UG/VG)是比较基础的内容,有关代码可以看一下这两个博客:

+ +

matlab版本

+ +

python版本

+ +

数据页面显示的信息

+ +

至于ADT和SSH和SLA的关系,可以看一下博客: +What are the differences between the SSH and SLA?

+ +

Scheme representation

+ +

数据在线可视化

+ +

在初步了解了数据的基本信息,提供的变量类型以及具体属性之后,网站还提供了非常良好的交互式地帮助我们在线可视化变量的工具,点击“view products”即可进入:

+ +

数据页面显示的信息

+ +

在左边的面板,有“add layers”,可以选择画图需要的数据,,可以更改colormap的类型,更改显示的日期等等。

+ +

东西向地转流速示意图

+ +

数据下载

+ +

同时,我们还发现在左边的面板上有个下载的按钮(

+ +

下载图

+ +

下方可以设置选择研究的区域以及选择的时间范围,研究区域的选取可以在地图上选择一个矩形区域或者手动输入东西南北边界来获得:

+ +

数据范围选择

+ +

如图所示,这个网站提供了直接下载(紫色圆圈)和API下载两种方式(红色圆圈),前者更加简便,后者可以提供较为定制化地下载选择。

+ +

其中,如果我们想要下载东经100°到东经150°,北纬20°到40°,时间跨度是2022年2月份的海表面东西方向的地转速度,只需要在配置好MOTU服务的基础上,在命令行输入以下指令即可把把数据下载到指定文件夹(OUTPUT_DIRECTORY):

+ +
python -m motuclient --motu https://my.cmems-du.eu/motu-web/Motu --service-id SEALEVEL_GLO_PHY_L4_MY_008_047-TDS --product-id cmems_obs-sl_glo_phy-ssh_my_allsat-l4-duacs-0.25deg_P1D --longitude-min 100 --longitude-max 150 --latitude-min 20 --latitude-max 40 --date-min "2022-02-01 00:00:00" --date-max "2022-02-28 23:59:59" --variable ugos --out-dir <OUTPUT_DIRECTORY> --out-name <OUTPUT_FILENAME> --user <USERNAME> --pwd <PASSWORD>
+
+ +

code

+ +

同时,如果我们想要批量下载该研究区域其他时间段或者更大时间尺度范围的海表流场信息,只需要对该段代码进行细微的改动或者增加循环的方式即可!

+ +

可能遇到的问题

+ +
    +
  1. 网络问题和其他 + + +

    --proxy-server <PROXY_SERVER_URL>:<PROXY_PORT_NUMBER>

    + + + +

    --proxy-user <PROXY_USERNAME> --proxy-pwd <PROXY_PASSWORD>

    +
  2. +
  3. +

    什么是MOTU Client以及需要的python环境要求?

    + +

    提防python版本问题

    +
  4. +
+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/6d/aae6c2a6c9c8e14eee5d866ec1c871c0efbee5c545c4a7e6f2cdf88f190e2e b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/6d/aae6c2a6c9c8e14eee5d866ec1c871c0efbee5c545c4a7e6f2cdf88f190e2e new file mode 100644 index 0000000000..b8710509ff --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/6d/aae6c2a6c9c8e14eee5d866ec1c871c0efbee5c545c4a7e6f2cdf88f190e2e @@ -0,0 +1,2 @@ +I"`

这一讲主要是介绍了set(集合)数据结构,三元运算符等等内容。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/6e/0e8473db248dc8859b448e5ecc48e1dc1e6e631b55742f1da1f6664244939c b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/6e/0e8473db248dc8859b448e5ecc48e1dc1e6e631b55742f1da1f6664244939c new file mode 100644 index 0000000000..585181dccd --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/6e/0e8473db248dc8859b448e5ecc48e1dc1e6e631b55742f1da1f6664244939c @@ -0,0 +1,27 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/6f/54bb3d577dd69d0a677ef98ecf3dbac15db2c2fee0b7e950806b525db6aeb8 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/6f/54bb3d577dd69d0a677ef98ecf3dbac15db2c2fee0b7e950806b525db6aeb8 new file mode 100644 index 0000000000..98e94db2a0 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/6f/54bb3d577dd69d0a677ef98ecf3dbac15db2c2fee0b7e950806b525db6aeb8 @@ -0,0 +1,348 @@ +I"(-

主要包括pytest安装;pytest创建简单的测试用例;pytest基础使用如函数级别的方法和类级别的方法;pytest常见插件如pytest-html将测试结果以html的形式展示出来,pytest-rerunfailures可以进行测试代码的失败重试;pytest的数据参数化,可以实习同样的测试用例函数对多个输入的测试数据的测试;最后是根据pytest运行原则以及pytest.ini指定测试的文件夹,测试函数等对之前的接口模块函数进行重构,进行自动化测试。

+ +

pytest安装

+ +

在requirements.txt中添加pytest

+
pytest~=4.5.0
+
+ +

在项目根目录下执行

+
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
+
+ +

pytest使用

+ +

创建简单的测试用例

+ +

在testcase文件夹下面创建文件夹t_pytest,然后在t_pytest文件夹下创建pytest_demo.py文件,内容如下:

+ +

+import pytest
+
+#1、创建简单测试方法
+"""
+1、创建普通方法
+2、使用pytest断言的方法
+"""
+#普通方法
+def func(x):
+    return x+1
+
+#断言的方法
+def test_a():
+    print("---test_a----")
+    assert func(3) == 5  #断言失败
+
+def test_b():
+    print("---test_b----")
+    assert 1 #断言
+
+
+#2、pytest运行
+"""
+1、Pycharm代码直接执行
+2、命令行执行
+"""
+
+#代码直接执行
+if __name__ == "__main__":
+    pytest.main(["pytest_demo.py"])
+
+ +

在Pycharm中直接运行pytest_demo.py文件,还可以增加一些参数:

+
#代码直接执行
+import pytest
+if __name__ == "__main__":
+    pytest.main(["-s","pytest_demo.py"]) # -s参数可以打印print的内容
+
+

在命令行中执行代码,操作如下:

+
# 进入到项目根目录下
+cd testcase/t_pytest
+# 执行代码
+python pytest_demo.py
+
+

测试结果一个通过,一个失败

+ +
============================= test session starts ==============================
+collecting ... collected 2 items
+
+pytest_demo.py::test_a FAILED                                            [ 50%]---test_a----
+
+testcase/t_pytest/pytest_demo.py:13 (test_a)
+4 != 5
+
+预期:5
+实际:4
+<点击以查看差异>
+
+def test_a():
+        print("---test_a----")
+>       assert func(3) == 5  #断言失败
+E       assert 4 == 5
+E        +  where 4 = func(3)
+
+pytest_demo.py:16: AssertionError
+
+
+
+
+pytest_demo.py::test_b PASSED                                            [100%]---test_b----
+
+
+========================= 1 failed, 1 passed in 0.04s ==========================
+
+进程已结束,退出代码1
+
+ +

pytest的基础使用

+ +

函数级别的方法

+ +

运行于测试方法的始末,运行一次测试函数会运行一次 setup 和 teardown。

+ +

在t_pytest文件夹下创建pytest_func.py文件,内容如下:

+ +

+import pytest
+"""
+1、定义类
+2、创建测试方法test开头
+3、创建setup,teardown
+4、运行查看结果
+
+"""
+
+#1、定义类
+class TestFunc:
+    #3、创建setup,teardown
+    def setup(self):
+        print("---setup----")
+
+    def teardown(self):
+        print("---teardown---")
+
+    #2、创建测试方法test开头
+    def test_a(self):
+        print("test_a")
+
+    def test_b(self):
+        print("test_b")
+
+#4、运行查看结果
+if __name__ =="__main__":
+    pytest.main(["-s","pytest_func.py"])
+
+ +

返回控制台结果如下:

+ +
Testing started at 上午10:37 ...
+Launching pytest with arguments /home/hugo/PycharmProjects/InterAutoTest_W/testcase/t_pytest/pytest_func.py --no-header --no-summary -q in /home/hugo/PycharmProjects/InterAutoTest_W/testcase/t_pytest
+
+============================= test session starts ==============================
+collecting ... collected 2 items
+
+pytest_func.py::TestFunc::test_a 
+pytest_func.py::TestFunc::test_b 
+
+======================== 2 passed, 4 warnings in 0.01s =========================
+
+进程已结束,退出代码0
+---setup----
+PASSED                                  [ 50%]test_a
+---teardown---
+---setup----
+PASSED                                  [100%]test_b
+---teardown---
+
+ +

我们发现,这里在运行每个测试方法之前都会运行setup方法,运行完每个测试方法之后都会运行teardown方法。

+ +

类级别方法

+ +

运行于测试类的始末,在一个测试内只运行一次 setup_class 和 teardown_class,不关心测试类内有多少个测试函数。

+ +

在t_pytest文件夹下创建pytest_class.py文件,内容如下:

+ +

+import pytest
+"""
+1、定义类
+2、创建测试方法test开头
+3、创建setup_class,teardown_class
+4、运行查看结果
+
+"""
+
+#1定义类
+class TestClass:
+    #3、创建setup_class,teardown_class
+    def setup_class(self):
+        print("---setup_class---")
+
+    def teardown_class(self):
+        print("---teardown_class---")
+
+    #2、创建测试方法test开头
+    def test_a(self):
+        print("test_a")
+
+    def test_b(self):
+        print("test_b")
+
+#4、运行查看结果
+if __name__ =="__main__":
+    pytest.main(["-s","pytest_class.py"])
+
+ +

返回信息如下:

+ +
============================= test session starts ==============================
+collecting ... collected 2 items
+
+pytest_class.py::TestClass::test_a ---setup_class---
+PASSED                                [ 50%]test_a
+
+pytest_class.py::TestClass::test_b PASSED                                [100%]test_b
+---teardown_class---
+
+
+============================== 2 passed in 0.01s ===============================
+
+进程已结束,退出代码0
+
+ +

我们发现,无论执行多少个测试方法,setup_class和teardown_class方法只会执行一次。

+ +

pytest常用插件

+ +

pytest-html

+ +

测试报告插件,可以将测试结果以html的形式展示出来。

+ +

安装pytest-html插件,在requirements.txt文件中添加pytest-html,然后在终端中运行pip install -r requirements.txt安装插件。

+ +

使用,在配置文件pytest.ini中的命令行参数中增加 –html=用户路径/report.html

+ +
[pytest]
+addopts = -s --html=../../report/report.html
+
+ +

执行之前写好的pytest_demo.py文件,查看生成的测试报告。./../report是回到根目录下,创建一个report文件夹,report.html是生成的测试报告。

+ +

失败重试

+ +

自动化测试脚本可能会使用到网络,如果网络不好可能最终会使脚本不通过。像这种情况可能并不是脚本本身的问题,仅仅是因为网络忽快忽慢,那么我们可以使用失败重试的插件,当失败后尝试再次运行。一般情况最终成功可以视为成功,但最好进行进行排查时候是脚本问题。

+ +

安装方法:在requirements.txt文件中添加pytest-rerunfailures,然后在终端中运行pip install -r requirements.txt安装插件。

+ +

使用方法:在配置文件pytest.ini中的命令行参数中增加 –reruns=3,表示失败之后重试3次。重试时,如果脚本通过,那么后续不再重试。

+ +
[pytest]
+addopts =-s --html=../../report/report.html --reruns 3
+
+ +

也可以加reruns-delay参数,表示失败之后重试的间隔时间。

+ +
[pytest]
+addopts =-s --html=../../report/report.html --reruns 3 --reruns-delay 2
+
+ +

除了在pytest.ini中配置,也可以添加mark标记,在测试用例中添加@mark.flaky(reruns=3,reruns_delay=2),可以只对某个测试用例进行失败重试。 +@mark是装饰器,flaky表示标记。

+ +

数据参数化

+ +

应用场景

+ +

登录功能都是输入用户名,输入密码,点击登录。但登录的用户名和密码如果想测试多个值是没有办法用普通的操作实现的。数据参数化可以帮我实现这样的效果。

+ +

方法名 +pytest.mark.parametrize(argnames,argvalues,indirect=False,ids=None,scope=None)

+ +

argnames:参数名

+ +

argvalues:参数对应值,类型必须为可迭代类型,一般使用list

+ +

indirect:是否对参数值进行间接调用,如果为True,那么参数值必须是函数名,否则报错;

+ +

ids:参数值对应的id,如果不指定,那么默认使用参数值,如果指定,那么参数值必须和ids的长度一致,否则报错;

+ +

scope:参数值的作用域,可以是function、class、module、session,如果不指定,那么默认为function.

+ +

以下是传入单个参数的例子:

+ +
import pytest
+
+"""
+1、创建类和测试方法
+2、创建数据
+3、创建参数化
+4、运行
+"""
+#1、创建类和测试方法
+class TestDemo:
+    # 2、创建测试数据
+    data_list = ["xiaoming","xiaohong"]
+
+    # 3、参数化
+    @pytest.mark.parametrize("name",data_list)
+    
+    # 1、创建类和测试方法
+    def test_a(self,name):
+        print("test_a")
+        print(name)
+        assert 1
+
+if __name__ == "__main__":
+    pytest.main(["pytest_one.py"])
+
+ +

以下是多个参数的例子:多个参数是以元祖的方式传入的,元祖中的每个元素对应一个参数。

+
@pytest.mark.parametrize((‘param1’,’param2’),[(value1[0],value1[1]),(value2[0],value2[1])])
+
+ +
import pytest
+
+"""
+1、创建类和测试方法
+2、创建数据
+3、创建参数化
+4、运行
+"""
+#1、创建类和测试方法
+class TestDemo:
+    #2、创建测试数据
+    data_list = [("xiaoming","123456"),("xiaohong","456789")]
+
+    #3、参数化
+    @pytest.mark.parametrize(("name","password"),data_list)
+    def test_a(self,name,password):
+        print("test_a")
+        print(name,password)
+        assert 1
+
+if __name__ == "__main__":
+    pytest.main(["pytest_two.py"])
+
+ +

利用pytest来进行接口自动化测试

+ +

pytest运行原则:在不指定运行目录,运行文件,运行函数等参数的默认情况下,pytest会执行当前目录下所有以test为前缀(test.py)或者以_test结尾(_test.py)的文件中以test开头的测试方法。

+ +

因此,我们需要根据默认运行原则,调整py文件命名,函数命名(比如所之前的login():改成test_login():,也就算重构我们之前的五个接口模块。 然后,pytest.main()运行,或者命令行直接pytest运行。

+ +

另外,我们还可以通过pytest.ini文件来配置运行参数,比如指定运行文件,运行函数等。

+ +

pytest.ini文件内容如下:

+ +
[pytest]
+# 指定运行路径
+testpaths = ./testcase
+# 指定执行目录下的文件
+python_files = test_*.py
+# 指定执行测试类
+python_classes = Test*
+# 指定执行测试方法
+python_functions = test_*
+
+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/71/399fec69e0edfa2680f147ed854192127986d74b4f63298f73a6d304413c89 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/71/399fec69e0edfa2680f147ed854192127986d74b4f63298f73a6d304413c89 new file mode 100644 index 0000000000..53bc5df1a3 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/71/399fec69e0edfa2680f147ed854192127986d74b4f63298f73a6d304413c89 @@ -0,0 +1,91 @@ +I"

Markdowm插件Office Viewer介绍以及vscode+PicGO+Github搭建个人博客图床

+ +

Markdowm插件

+ + + +

图片、表格、代码块,列表(有序、无序、任务)、引用、分隔、斜体等傻瓜式操作;方便快速写标题以及大纲视图;导出PDF极其方便;最上方预览小按钮点击之后进入预览界面,可以选择桌面或者手机端浏览,可以把写好的内容直接发布到微信公众号或者知乎;CSDN博客的内容如果是用markdown写的,直接复制进来,格式都不用调整。

+ +

office_viewer使用示意图

+ +

PicGO介绍

+ +

vscode上有一个picgo插件,可以让我们用快捷键即可上传图片到默认的免费服务器,具体的使用方法是,安装完成后:

+ +
    +
  1. Ctrl+Alt+U 从剪切板粘贴图片,上传到服务器;
  2. +
  3. Ctrl+Alt+E 打开资源管理器,选择图片。
  4. +
+ +

picgo默认上传的服务器是 SM.MS服务器,由于你懂得原因,国内的访问速度让人抓狂,但是picgo支持github以及阿里云作为图床。

+ +

其中,github图床最为稳定,且一个仓库上限是100G,只做图床绰绰有余。最糟糕的一点是速度,但是使用cdn加速之后速度还是可以的。

+ +

Github图床设置

+ +
    +
  1. 打开github,创建新的public仓库,仓库名称可以设置为PicgoBed
  2. +
  3. +

    进入个人设置页面,选择开发者设置

    + +

    github_setting

    +
  4. +
  5. +

    选择Personal access tokens,选择生成新token,此token是图床上传时验证身份用的;添加描述,将repo选上,Expiration选择no expiration

    + +

    tokens

    +
  6. +
  7. 将生成的字符串保存,关闭页面后将再也无法看到这个字符串了
  8. +
+ +

PicGO设置

+ +

GitHub设置完之后,我们需要修改picgo插件的设置,把服务器编程我们的图床,具体设置如下:

+ +
    +
  1. vscode右下角选择设置,搜索设置里面输入PicGO,打开扩展里面的PicGO
  2. +
  3. 设置如下,github token写我们刚才复制下来的
  4. +
+ +

picgosetting

+ + + +

其他可能出现的问题的处理:

+ + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/73/bf7ce919aad37ede3b68422e6412301d59231379af8b15d68347df9b1b1a39 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/73/bf7ce919aad37ede3b68422e6412301d59231379af8b15d68347df9b1b1a39 new file mode 100644 index 0000000000..9234863b44 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/73/bf7ce919aad37ede3b68422e6412301d59231379af8b15d68347df9b1b1a39 @@ -0,0 +1,207 @@ +I"

1. 数据来源

+ +

和鲸社区-太湖站2001-2006年水质监测数据集

+ +

包括两个文件:

+ +
    +
  1. 太湖站水质观测站点数据.xlsx
  2. +
  3. 2001-2006年太湖水体物理及水体化学监测数据.xlsx
  4. +
+ +

2. 站点分布示意图

+ +
import pandas as pd
+import folium
+import matplotlib.pyplot as plt
+# 1. 读取数据
+df = pd.read_excel(r'.\太湖站水质观测站点数据.xlsx')
+print(df.head())
+
+# Drop the second row of the dataframe to remove the header
+df = df.drop([0])
+# Reset the index of the dataframe
+df = df.reset_index(drop=True)
+
+
+# 2. 画出站点分布
+
+# Create a map object centered at (31.3, 120.6)
+# Set the range of longitude and latitude for the map
+east_min = df['东经'].min() - 5
+east_max = df['东经'].max() + 5
+north_min = df['北纬'].min() - 5
+north_max = df['北纬'].max() + 5
+
+# Create a map object centered at  with the new range of longitude and latitude
+map = folium.Map(location=[(north_max+north_min)/2, (east_max+east_min)/2], zoom_start=10,  control_scale=True, tiles='Stamen Terrain')
+# tiles表示地图的样式;max_bounds表示地图的最大范围;control_scale表示是否显示比例尺
+# Add markers to the map for each observation station
+for index, row in df.iterrows():
+    folium.Marker(location=[row['北纬'], row['东经']], tooltip=row['观测站代码']).add_to(map)
+
+# Display the map
+map
+
+# Save the map as an HTML file
+map.save('map.html')
+
+ +

20230404192804

+ +

3. 物理性质数据分析

+ +

对水深随着时间变化进行探究。

+ +
import pandas as pd
+import matplotlib.pyplot as plt
+# 读入2001-2006年太湖水体物理及水体化学监测数据.xlsx中的表格2001-2006年太湖水体物理监测数据
+df2 = pd.read_excel(r'.\2001-2006年太湖水体物理及水体化学监测数据.xlsx', sheet_name='2001-2006年太湖水体物理监测数据')
+df2 = df2.drop([0])
+# Reset the index of the dataframe
+df2 = df2.reset_index(drop=True)
+# Round the values in the '年' column to the nearest integer
+df2['年'] = df2['年'].astype(int)
+df2['月'] = df2['月'].astype(int)
+# Combine the '年' and '月' columns into a single 'date' column in df2
+df2['date'] = pd.to_datetime({'year': df2['年'], 'month': df2['月'], 'day': 1})
+print(df2.head())
+
+# 画出水深随时间变化的折线图
+df2_THL00 = df2[df2['站点'] == 'THL00']
+df2_THL00 = df2_THL00.set_index('date')
+df2_THL00['水深'].plot()
+# title表示图的标题;xlabel表示x轴的标题;ylabel表示y轴的标题
+plt.title('THL00 Water Depth')
+plt.xlabel('Date')
+plt.ylabel('Water Depth (m)')
+
+# 画出所有的站点水深随时间变化的折线图
+for station in df2['站点'].unique():
+    df2_station = df2[df2['站点'] == station]
+    df2_station = df2_station.set_index('date')
+    df2_station['水深'].plot()
+    plt.title(station + ' Water Depth')
+    plt.xlabel('Date')
+    plt.ylabel('Water Depth (m)')
+    plt.show()
+
+# 画在一张图上
+# 设置图的大小
+plt.figure(figsize=(10, 10))
+for station in df2['站点'].unique():
+    df2_station = df2[df2['站点'] == station]
+    df2_station = df2_station.set_index('date')
+    df2_station['水深'].plot()
+plt.title('Water Depth')
+plt.xlabel('Date')
+plt.ylabel('Water Depth (m)')
+# 设置legend的位置在图的外面
+plt.legend(df2['站点'].unique(), loc='center left', bbox_to_anchor=(1, 0.5))
+plt.show()
+
+ +

输出结果部分:

+ +

20230404193002

+ +

20230404193024

+ +

20230404193054

+ +

4. 化学性质数据分析

+ +

对PH值进行探究,使用克里金插值的方法。

+ +
import pandas as pd
+# 读入2001-2006年太湖水体物理及水体化学监测数据.xlsx中的表格2001-2006年太湖水体物理监测数据
+df3 = pd.read_excel(r'.\2001-2006年太湖水体物理及水体化学监测数据.xlsx', sheet_name='2001-2006年太湖水体化学监测数据')
+df3 = df3.drop([0])
+# Reset the index of the dataframe
+df3 = df3.reset_index(drop=True)
+# Round the values in the '年' column to the nearest integer
+df3['年'] = df3['年'].astype(int)
+df3['月'] = df3['月'].astype(int)
+# Combine the '年' and '月' columns into a single 'date' column in df3
+df3['date'] = pd.to_datetime({'year': df3['年'], 'month': df3['月'], 'day': 1})
+df3=df3.set_index('date')
+print(df3.head())
+
+
+# 读取站点位置
+df = pd.read_excel(r'.\太湖站水质观测站点数据.xlsx')
+# Drop the second row of the dataframe to remove the header
+df = df.drop([0])
+# Reset the index of the dataframe
+df = df.reset_index(drop=True)
+
+# Rename the columns
+df['站点'] = df['观测站代码']
+# drop columns
+df = df.drop(['观测站序号','观测站代码'], axis=1) # axis=1表示列
+print(df.head())
+
+# 提取df3中的两列,分别是站点和PH值
+df3_ph = df3[['站点', 'pH']]
+
+df3_ph_20010101 = df3_ph[df3_ph.index == '2001-01-01']
+
+# 根据站点拼接df3_ph_20010101和df
+df_merged = pd.merge(df, df3_ph_20010101, on='站点', how='left')
+
+# 克里金插值,df_merged中的ph
+# 克里金插值需要使用外部库pykrige,需要先安装
+# pip install pykrige
+
+# 导入外部库
+import numpy as np
+from pykrige.ok import OrdinaryKriging
+
+
+# 将df_merged中的经纬度和pH值提取出来
+X = df_merged[['东经', '北纬']].values
+y = df_merged['pH'].values
+
+# 设置克里金插值的网格范围
+grid_lon = np.arange(df_merged['东经'].min()-0.1, df_merged['东经'].max()+0.1, 0.01)
+grid_lat = np.arange(df_merged['北纬'].min()-0.1, df_merged['北纬'].max()+0.1, 0.01)
+
+
+# Generate ordinary kriging object
+OK = OrdinaryKriging(
+    np.array(X[:, 0]),
+    np.array(X[:, 1]),
+    y,
+    variogram_model = "linear",
+    verbose = False,
+    enable_plotting = False,
+    coordinates_type = "euclidean",
+)
+
+# 进行克里金插值
+z, ss = OK.execute('grid', grid_lon, grid_lat) 
+
+# 将插值结果可视化
+plt.figure(figsize=(10, 10))
+# pcolor of z
+plt.pcolor(grid_lon, grid_lat, z, cmap='jet')
+plt.colorbar()
+#plt.scatter(X[:, 0], X[:, 1], c=y, cmap='jet', edgecolor='k')
+plt.title('pH Interpolation')
+plt.xlabel('Longitude')
+plt.ylabel('Latitude')
+plt.show()
+
+ +

部分结果输出如下:

+ +

1680608067436

+ +

1680608067436

+ +

20230404193505

+ +

克里金插值结果如下:(感觉是错的,不改了)

+ +

20230404193731

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/74/343a67d62c7266afee8847734ba8d326a307a68e34b1b6b55e81fe7c040aa9 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/74/343a67d62c7266afee8847734ba8d326a307a68e34b1b6b55e81fe7c040aa9 new file mode 100644 index 0000000000..cd98241a98 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/74/343a67d62c7266afee8847734ba8d326a307a68e34b1b6b55e81fe7c040aa9 @@ -0,0 +1,13 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/74/951bcce75676fc2548ca6b73dfc78df9f36df8be8a1630b4a4b708b30c645f b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/74/951bcce75676fc2548ca6b73dfc78df9f36df8be8a1630b4a4b708b30c645f new file mode 100644 index 0000000000..6ccdafcc1b --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/74/951bcce75676fc2548ca6b73dfc78df9f36df8be8a1630b4a4b708b30c645f @@ -0,0 +1,199 @@ +I"

libpython3.7m.so.1.0: /usr/local/lib/libpython3.7m.so.1.0

+ +

安装 MySQL

+ +
sudo apt update
+sudo apt install mysql-server
+
+ +

查看默认用户名和密码

+ +

新版的MySQL安装后,默认用户名不是root,为了方便,一般我们需要修改成我们想要的用户名和密码。进入配置文件:

+ +
vim /etc/mysql/debian.cnf 
+
+ +

结果如下:

+ +

debian.cnf结果

+ +

默认的密码为:

+ +
user     = debian-sys-maint
+password = VDgQcUjzyfPfgxbA
+
+ +

登录数据库并修改用户名和密码

+ +
mysql -udebian-sys-maint -p
+
+ +

查看mysql中默认数据库:(记得sql结尾要加分号)

+ +
show databases;
+
+ +

show databases results

+ +

使用数据库mysql:

+ +
use mysql;
+
+ +

use mysql results

+ +

将root密码设置为空:

+ +
update user set authentication_string='' where user='root'; 
+
+ +

清空root用户默认的密码:

+ +
select user,authentication_string from user where user='root';
+
+ +

清空root用户默认的密码

+ +

为root设置密码:ALTER user

+ +
ALTER user 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';
+
+ +

查看设置root用户和密码:

+ +
select user,authentication_string from user where user='root';
+
+ +

查看设置root用户和密码

+ +

退出SQL:

+ +
quit;
+
+ +

在mysql8.0之后的版本中,password函数已被取消,加密方式不再使用mysql_native_password,换成了caching_sha2_password.

+ +

原本的sql语句为:

+ +
UPDATE user SET authentication_string=password("密码") WHERE user="root";
+1
+
+ +

对应的写法可以改为:

+ +
UPDATE user SET authentication_string=SHA1("密码") WHERE user="root";
+
+ +

配置MySQL远程登陆

+ +
#修改配置文件,注释掉 bind-address = 127.0.0.1
+$ sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf
+
+# 保存退出,然后进入mysql服务,执行授权命令:(输入密码123456)
+$ mysql -uroot -p
+
+ +

授权命令

+ +

localhost即本地主机的域名,如同:www.baidu.com ,也是本机ip地址,整个127.* 网段通常被用作loopback网络接口的默认地址,按照惯例通常设置为127.0.0.1。我们当前这个主机上的这个地址,别人不能访问,即使访问,也是访问自己。因为每一台TCP/IP协议栈的设备基本上都有local/127.0.0.1.

+ +

进入数据库之后,查看当前主机配置信息为localhost,需要修改为%:(目的是为了让所有主机都能访问)

+ +
use mysql;
+select host from user where user='root';
+
+ +

将Host设置为通配符%:

+ +
update user set host = '%' where user ='root';
+
+ +

修改后要刷新一下:

+ +
flush privileges;
+
+ +

查看是否修改成功:

+ +
select host from user where user='root';
+
+ +

已经修改成功

+ +

退出数据库后重启数据库服务:(然后就可以在数据库工具远程MySQL数据库了。)

+ +
sudo /etc/init.d/mysql restart #重启mysql服务
+
+ +

在远程数据库远程连接MySQL数据库了

+ +

首先是进行测试,无论您如何安装它,MySQL 都应该自动开始运行。要对此进行测试,请检查其状态。

+ +
sudo systemctl status mysql
+
+ +

MySQL已经自动运行

+ +

然后,使用 ss 命令检查开放端口:

+ +
sudo ss -ltn 
+
+ +

开放端口如下

+ +

可以看出,我们的服务器正在侦听端口 3306 和 33060 上的连接。这些是与 HTTP 和 MySQL 相关的端口。

+ +

在我们的本地系统上使用ss或者nmap localhost命令时,我们绕过了防火墙。实际上,这些命令显示处于侦听状态的端口,但这并不一定意味着端口对 Internet 开放,因为我们的防火墙可能拒绝连接。

+ +

使用以下命令检查 ufw 防火墙的状态。

+ +
sudo ufw status verbose
+
+ +

可以发现,防火墙状态为不活动。

+ +

1677200490189

+ +

如果防火墙状态为active状态, ufw 拒绝传入连接。由于端口 80 和 3306 尚未添加为例外,因此 HTTP 和 MySQL 无法接收传入连接,尽管ss并nmap报告它们处于侦听状态。

+ +

让我们使用以下命令为这些端口添加例外。

+ +
sudo ufw allow 80/tcp
+sudo ufw allow 3306/tcp
+
+ +

navicat安装破解教程

+ +

使用ifconfig查看服务器ip地址,然后在navicat中连接数据库。

+ +

连接数据库 +连接结果如下:

+ +

连接结果

+ +

也可以用VSCode来管理MySql数据库:

+ +
    +
  1. 点击左侧扩展插件,然后搜索下MySql;
  2. +
  3. 安装过后,我们就需要连接了,直接快捷键,Ctrl+Shift+P,打开窗口;
  4. +
  5. 设置如下: +
      +
    1. 设置如下
    2. +
    3. 端口号默认3306
    4. +
    5. 连接结果如下:连接结果如下
    6. +
    +
  6. +
+ +

参考链接

+ +

Ubuntu安装MySQL8.0,并用Navicat远程连接_任求其-zzx的博客-CSDN博客

+ +

Ubuntu 20.04 安装 MySQL 8.0 并且远程连接数据库(包括后续遇到的新坑)_Nymph2333的博客-CSDN博客_executing /lib/systemd/systemd-sysv-install enable

+ +

在 Ubuntu 22.04 上安装 Python 3.9(多版本适用) - 知乎 (zhihu.com)

+ +

Index of python-local/3.7.12 (huaweicloud.com)

+ +

在Ubuntu 18.04上安装不同版本的python及选择默认Python_WMLCOLIN的博客-CSDN博客

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/75/3c88e5609975d95219cc90dd345f64a43b37ecd09b5cf04cc7acdf753c9e16 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/75/3c88e5609975d95219cc90dd345f64a43b37ecd09b5cf04cc7acdf753c9e16 new file mode 100644 index 0000000000..012b967929 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/75/3c88e5609975d95219cc90dd345f64a43b37ecd09b5cf04cc7acdf753c9e16 @@ -0,0 +1,18 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/75/ce283c59e3d6a53016cac19bfe1d45faa26da27c7577b0da4de534a6a38f0e b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/75/ce283c59e3d6a53016cac19bfe1d45faa26da27c7577b0da4de534a6a38f0e new file mode 100644 index 0000000000..a9414cdaaf --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/75/ce283c59e3d6a53016cac19bfe1d45faa26da27c7577b0da4de534a6a38f0e @@ -0,0 +1,184 @@ +I"

音乐-起风了

+ +

https://link.jscdn.cn/sharepoint/aHR0cHM6Ly9oa3VzdGNvbm5lY3QtbXkuc2hhcmVwb2ludC5jb20vOnU6L2cvcGVyc29uYWwvamNoZW5mbF9jb25uZWN0X3VzdF9oay9FVE1tNEYwRU1CSkt0UGJKNDlmbkFVWUJWdUZXYUJ0RzVZTlVoRlFGV0FtY2NBP2U9RlZ4bkxB.mp3

+ +

要求

+ + + +

开始使用

+ +

step1

+ +

首先下载这个文件open-embed.html,右键,另存为 Html;下面是源码,也可以复制保存下面的源码;

+ +
<style>
+.videoWrapper {
+	position: relative;
+	padding-bottom: 56.333%;
+	height: 0;
+    background: black;
+}
+.videoWrapper iframe {
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+    border: 0;
+}  
+</style>
+
+<script>
+function get_youtube_id(url) {
+    var p = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;
+    return (url.match(p)) ? RegExp.$1 : false;
+}
+function vimeo_embed(url,el) {
+    var id = false;
+    $.ajax({
+      url: 'https://vimeo.com/api/oembed.json?url='+url,
+      async: true,
+      success: function(response) {
+        if(response.video_id) {
+          id = response.video_id;
+          if(url.indexOf('autoplay=1') !== -1) var autoplay=1; else var autoplay=0;
+          if(url.indexOf('loop=1') !== -1) var loop=1; else var loop=0;
+          var theInnerHTML = '<div class="videoWrapper"><iframe src="https://player.vimeo.com/video/'+id+'/?byline=0&title=0&portrait=0';
+          if(autoplay==1) theInnerHTML += '&autoplay=1';
+          if(loop==1) theInnerHTML += '&loop=1';
+          theInnerHTML += '" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe></div>'; 
+          el.innerHTML = theInnerHTML;
+        }
+      }
+    });
+}
+function video_embed() {
+    var p = document.getElementsByTagName('p');
+    for(var i = 0; i < p.length; i++) {
+        //check if this is an external url (that starts with https:// or http://
+        if (p[i].innerHTML.indexOf("http://") == 0 ||
+            p[i].innerHTML.indexOf("https://") == 0) {
+            var youtube_id = get_youtube_id(p[i].innerHTML);
+            if(youtube_id) {
+                if(p[i].innerHTML.indexOf('autoplay=1') !== -1) var autoplay=1; else var autoplay=0;
+                if(p[i].innerHTML.indexOf('loop=1') !== -1) var loop=1; else var loop=0;
+                var theInnerHTML = '<div class="videoWrapper"><iframe width="720" height="420" src="https://www.youtube.com/embed/' + youtube_id + '?rel=0&showinfo=0';
+                if(autoplay==1) theInnerHTML += '&autoplay=1';
+                if(loop==1) theInnerHTML += '&loop=1&playlist='+youtube_id+'&version=3';
+                theInnerHTML += '" frameborder="0" allowfullscreen></iframe></div>';
+                p[i].innerHTML = theInnerHTML;
+            }
+            if(p[i].innerHTML.indexOf('vimeo.com') !== -1) {
+                //ask vimeo for the id and place the embed
+                vimeo_embed(p[i].innerHTML,p[i]);
+            }
+        }
+    }
+}
+video_embed();
+
+function mp3_embed() {
+    var p = document.getElementsByTagName('p');
+    var song_format = ['.mp3','.m4a','.wma']; // 音乐格式,判断是否属于这几个格式
+    for(var i = 0; i < p.length; i++) {
+        for(var j=0;j<song_format.length;j++){
+            if(p[i].innerHTML.toLowerCase().indexOf(song_format[j]) !== -1) {
+            var str = p[i].innerHTML.split('?');
+            if(str.length == 1) str[1] = '';
+            var str1 = str[1];
+            str1 = str1.replace('&','').replace('&','');
+            str1 = str1.replace('autoplay=1','').replace('autoplay=0','');
+            str1 = str1.replace('loop=1','').replace('loop=0','');
+            str1 = str1.replace('controls=0','').replace('controls=1','');
+
+            if (str[0].toLowerCase().lastIndexOf(song_format[j], str[0].length - 4) === str[0].length - 4 && str1.length == 0) {
+                if(str[1].indexOf('autoplay=1') !== -1) var autoplay=1; else var autoplay=0;
+                if(str[1].indexOf('loop=1') !== -1) var loop=1; else var loop=0;
+                if(str[1].indexOf('controls=0') !== -1) var controls=0; else var controls=1;
+                var newInnerHTML = '<audio';
+                if(autoplay==1) newInnerHTML += ' autoplay';
+                if(loop==1) newInnerHTML += ' loop';
+                if(controls==1) newInnerHTML += ' controls';
+                newInnerHTML += '><source src="'+str[0]+'" type="audio/mpeg">Your browser does not support the audio element.</audio>';
+                p[i].innerHTML = newInnerHTML;
+            }
+            }
+        }
+
+    }
+}
+mp3_embed();
+</script>
+
+ +

step2

+ +

将上一步存储好的文件,保存到_includes目录下,就像下面这个样子

+ +

+ +

step3

+ +

_layouts目录下,default.html这个文件,打开,在最下面,找到对应的标签 </body> ,加入如下代码,注意:标签不要重复了

+ +

+ +

使用语法

+ +

借助七牛云之类的图床或云端存储,将需要的音乐文件上传至图床,并获得外链。

+ +

然后,在Markdown中直接使用:

+ +
<p>音乐外链url</p>
+
+ +

注意:音乐链接url增加了支持MP3、wma、m4a格式

+ +

Onedrive音乐文件获取外链

+ +

参考如下链接:

+ +

生成外链

+ +

先想办法把你要生成外链的音乐或者图片上传到OneDrive中。 +网页版的应该是被墙了,不科学上网用不了。 +现在的电脑上一般都有OneDrive的,自己搜一下。

+ +

在这里插入图片描述 +上图中我已经把两首歌放到OneDrive中了。

+ +

然后把文件共享:在文件上右击,点 共享

+ +

在这里插入图片描述

+ +

共享之后你就能看到这个,点左下角的按钮复制链接。

+ +

在这里插入图片描述

+ +

我复制到的链接如下:

+ +
+

https://1drv.ms/u/s!AjennNdWMahGijT1WWHNP-Wjj_CF?e=LNajCn

+
+ +

现在这样子肯定不能直接用了,需要借助转换工具。

+ +

可以直接用这个生成链接:

+ +

https://onedrive.gimhoy.com/

+ +

你输入之后就会可以生成了。

+ +

在这里插入图片描述

+ +

还有一个小工具可以批量生成

+ +

https://github.com/Mapaler/GetOneDriveDirectLink

+ +

https://mapaler.github.io/GetOneDriveDirectLink/

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/75/f166839fcf41f5fcd91694fdebe6dff0810b621f1ed632c444ac477f6acc9c b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/75/f166839fcf41f5fcd91694fdebe6dff0810b621f1ed632c444ac477f6acc9c new file mode 100644 index 0000000000..16b5011fa5 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/75/f166839fcf41f5fcd91694fdebe6dff0810b621f1ed632c444ac477f6acc9c @@ -0,0 +1,2 @@ +I"D

bash脚本测试Ubuntu服务器的性能并输出报告

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/76/14a9950ae3faee1f360721b484bcf7e4d6e33d8bdf68c9ef4a64c3f4d1ccb1 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/76/14a9950ae3faee1f360721b484bcf7e4d6e33d8bdf68c9ef4a64c3f4d1ccb1 new file mode 100644 index 0000000000..e2da2cdf23 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/76/14a9950ae3faee1f360721b484bcf7e4d6e33d8bdf68c9ef4a64c3f4d1ccb1 @@ -0,0 +1,2 @@ +I"!

代码

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/79/f6013351b8fda2b1a3306e17fa2d74bab6971113a4a2d5927b47b8448d3e92 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/79/f6013351b8fda2b1a3306e17fa2d74bab6971113a4a2d5927b47b8448d3e92 new file mode 100644 index 0000000000..7142b60d92 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/79/f6013351b8fda2b1a3306e17fa2d74bab6971113a4a2d5927b47b8448d3e92 @@ -0,0 +1,9 @@ +I"& + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/7a/75d4e541fa2cde30ba4939c1e9ac1598d2858635ee3e56ce6f512f99285530 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/7a/75d4e541fa2cde30ba4939c1e9ac1598d2858635ee3e56ce6f512f99285530 new file mode 100644 index 0000000000..6dbc161fe8 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/7a/75d4e541fa2cde30ba4939c1e9ac1598d2858635ee3e56ce6f512f99285530 @@ -0,0 +1,2 @@ +I"!

简介

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/7b/d5786b713525fd15f62b0cb4fd4154a422f8dab9e238838e0e233dfc82b60b b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/7b/d5786b713525fd15f62b0cb4fd4154a422f8dab9e238838e0e233dfc82b60b new file mode 100644 index 0000000000..d9215cf523 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/7b/d5786b713525fd15f62b0cb4fd4154a422f8dab9e238838e0e233dfc82b60b @@ -0,0 +1,2 @@ +I"B

下划线、双下划线

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/7c/8fdf8c5e407ae14d8f089388a98ca6edbbaaa17822490d2ce097298da031c8 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/7c/8fdf8c5e407ae14d8f089388a98ca6edbbaaa17822490d2ce097298da031c8 new file mode 100644 index 0000000000..571783dfdf --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/7c/8fdf8c5e407ae14d8f089388a98ca6edbbaaa17822490d2ce097298da031c8 @@ -0,0 +1,43 @@ +I"U + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/81/5825b0779fefc38d2c2ccca64ac0234f5707982313d15797a9214b2a0a43b5 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/81/5825b0779fefc38d2c2ccca64ac0234f5707982313d15797a9214b2a0a43b5 new file mode 100644 index 0000000000..b2e7e57e85 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/81/5825b0779fefc38d2c2ccca64ac0234f5707982313d15797a9214b2a0a43b5 @@ -0,0 +1,9 @@ +I"Y + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/81/cafdef9a37a00dda1e61d8b0095c065de6a48328e9b7769ec3db582cd9029a b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/81/cafdef9a37a00dda1e61d8b0095c065de6a48328e9b7769ec3db582cd9029a new file mode 100644 index 0000000000..0390e24c80 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/81/cafdef9a37a00dda1e61d8b0095c065de6a48328e9b7769ec3db582cd9029a @@ -0,0 +1,5 @@ +I"o

一个学不会地球流体力学的物理海洋学生瓜蛋子。

+ +

为啥想写博客

+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/82/31e44b713294aef928bdd73b43fe22afaa6345225e0b1556f2dc1b5927d0ad b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/82/31e44b713294aef928bdd73b43fe22afaa6345225e0b1556f2dc1b5927d0ad new file mode 100644 index 0000000000..c48fbfbe1d --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/82/31e44b713294aef928bdd73b43fe22afaa6345225e0b1556f2dc1b5927d0ad @@ -0,0 +1,2 @@ +I"

主要包括使用一维卷积神经网络(1D CNN)对安装在远程风机的高速轴承中的现场传感器采集到的时间序列数据通过短时傅里叶变化之后得到的频谱峰度(Kurtosis Spectogram)进行分类,得到每个样品的类别:中等寿命级(预期寿命大于15天的轴承——状态良好)以及短寿命轴承(预期寿命小于15天的轴承——状态不良)。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/85/0def218df037c02eeee1561a058a6a19343ca5995d03da6f95d68145cb54bf b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/85/0def218df037c02eeee1561a058a6a19343ca5995d03da6f95d68145cb54bf new file mode 100644 index 0000000000..9b60d0efd1 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/85/0def218df037c02eeee1561a058a6a19343ca5995d03da6f95d68145cb54bf @@ -0,0 +1,2 @@ +I"5

音乐-起风了

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/85/27e4718365c7e3600a86cde789b4bba7ea0217be256323998f0dd795f628fb b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/85/27e4718365c7e3600a86cde789b4bba7ea0217be256323998f0dd795f628fb new file mode 100644 index 0000000000..613f15b1d4 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/85/27e4718365c7e3600a86cde789b4bba7ea0217be256323998f0dd795f628fb @@ -0,0 +1,270 @@ +I",

主要包括python一些不太常见的知识点以及python高阶编程需要用到的知识点等等。 +比如说*args 和 **kwargs在函数传参以及函数调用中的用法,以及python中的装饰器等等。 +还比如说pdb在程序运行时查看变量的值,查看程序的执行流程,以及查看函数的调用栈。 +还例如,python中的Map、Reduce、Filter、Lambda等在函数式编程中的用法。

+ +

*args 和 **kwargs

+ +

其实并不是必须写成*args 和*kwargs。 只有变量前面的 *(星号)才是必须的. 你也可以写成*var 和*vars. 而写成*args 和**kwargs只是一个通俗的命名约定。

+ +

*args 和 **kwargs 主要用于函数定义。 你可以将不定数量的参数传递给一个函数。

+ +

预先并不知道, 函数使用者会传递多少个参数给你, 所以在这个场景下使用这两个关键字。

+ +

*args 是用来发送一个非键值对的可变数量的参数列表给一个函数.

+ +
def test_var_args(f_arg, *argv):
+    print("first normal arg:", f_arg)
+    for arg in argv:
+        print("another arg through *argv:", arg)
+
+test_var_args('yasoob', 'python', 'eggs', 'test')
+
+

这会产生如下输出:

+
first normal arg: yasoob
+another arg through *argv: python
+another arg through *argv: eggs
+another arg through *argv: test
+
+ +

**kwargs 允许你将不定长度的键值对, 作为参数传递给一个函数. 如果你想要在一个函数里处理带名字的参数, 你应该使用**kwargs.

+ +

举个例子:

+ +
def greet_me(**kwargs):
+    for key, value in kwargs.items():
+        print("{0} == {1}".format(key, value)) # .format()是python3的新特性, 用于格式化字符串,{0}表示第一个参数,{1}表示第二个参数
+        # 也可以用%来格式化字符串
+        print("%s == %s" % (key, value)) %s表示字符串,%d表示整数,%f表示浮点数
+gree_me(name="yasoob")
+
+

这会产生如下输出:

+
name == yasoob
+name == yasoob
+
+ +

使用 *args 和 **kwargs 来调用函数

+ +

那现在我们将看到怎样使用*args和**kwargs 来调用一个函数。 假设,你有这样一个小函数:

+
def test_args_kwargs(arg1, arg2, arg3):
+    print("arg1:", arg1)
+    print("arg2:", arg2)
+    print("arg3:", arg3)
+
+

我们可以使用下面的方式来调用这个函数:

+
# first with *args
+args = ("two", 3, 5)
+test_args_kwargs(*args)
+# now with **kwargs:
+kwargs = {"arg3": 3, "arg2": "two", "arg1": 5}
+test_args_kwargs(**kwargs)
+
+

这会产生如下输出:

+
arg1: two
+arg2: 3
+arg3: 5
+arg1: 5
+arg2: two
+arg3: 3
+
+

使用顺序

+ +

那么如果你想在函数里同时使用所有这三种参数, 顺序是这样的:

+
some_func(fargs, *args, **kwargs)
+
+ +
def test_args_kwargs(arg1, arg2, arg3, *args, **kwargs):
+    print("arg1:", arg1)
+    print("arg2:", arg2)
+    print("arg3:", arg3)
+    for arg in args:
+        print("another arg:", arg)
+    for kwarg in kwargs:
+        print("another keyword arg: {0} = {1}".format(kwarg, kwargs[kwarg]))
+
+ +

什么时候使用它们?

+

要看你的需求而定,最常见的用例是在写函数装饰器的时候,此外它也可以用来做猴子补丁(monkey patching)。猴子补丁的意思是在程序运行时(runtime)修改某些代码。

+ +

打个比方,你有一个类,里面有个叫get_info的函数会调用一个API并返回相应的数据。如果我们想测试它,可以把API调用替换成一些测试数据。例如:

+ +
import someclass # someclass是你定义的类
+
+def get_info(self, *args): # 这里的self是类的实例,*args是可变参数,可以传入任意多个测试数据
+    return "Test data"
+
+someclass.get_info = get_info
+
+ +

调试(Debugging)

+ +

利用好调试,能大大提高你捕捉代码Bug的。 +Python debugger(pdb)可以帮助我们在程序运行时查看变量的值,查看程序的执行流程,以及查看函数的调用栈。

+ +

pdb 模块定义了一个交互式源代码调试器,用于 Python 程序。它支持在源码行间设置(有条件的)断点和单步执行,检视堆栈帧,列出源码列表,以及在任何堆栈帧的上下文中运行任意 Python 代码。它还支持事后调试,可以在程序控制下调用。

+ +

调试器是可扩展的——调试器实际被定义为 Pdb 类。该类目前没有文档,但通过阅读源码很容易理解它。扩展接口使用了 bdb 和 cmd 模块。

+ +

pdb

+

从命令行运行

+

将 pdb.py 作为脚本调用,来调试其他脚本。你可以在命令行使用Python debugger运行一个脚本, 举个例子:

+
python -m pdb myscript.py
+
+

这会触发debugger在脚本第一行指令处停止执行。这在脚本很短时会很有帮助。你可以通过(Pdb)模式接着查看变量信息,并且逐行调试。

+ +

从脚本内部运行

+ +

同时,你也可以在脚本内部设置断点,这样就可以在某些特定点查看变量信息和各种执行时信息了。这里将使用pdb.set_trace()方法来实现。举个例子:

+
import pdb
+def make_bread():
+    pdb.set_trace() # 这里设置断点,程序会在这里停止,你可以查看运行到这里时的变量信息,然后按任意键继续执行,或者按c继续执行到下一个断点
+    return "I don't have time"
+
+

试下保存上面的脚本后运行之。你会在运行时马上进入debugger模式。现在是时候了解下debugger模式下的一些命令了。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
命令简写说明
helph显示帮助信息
listl查看当前行的代码段
continuec继续执行到下一个断点
quitq退出debugger
printp打印变量的值
argsa打印函数的参数
breakb设置断点
bt 打印函数的调用栈
returnr打印函数的返回值
nextn执行下一行代码
steps进入函数内部
+ +

单步跳过(next)和单步进入(step)的区别在于, 单步进入会进入当前行调用的函数内部并停在里面, 而单步跳过会(几乎)全速执行完当前行调用的函数,并停在当前函数的下一行。

+ +

Map,Filter 和 Reduce

+ +

Map,Filter 和 Reduce 三个函数能为函数式编程提供便利。我们会通过实例一个一个讨论并理解它们。

+ +

Map

+

Map会将一个函数映射到一个输入列表的所有元素上。这是它的规范:

+ +
map(function_to_apply, list_of_inputs)
+
+ +

大多数时候,我们要把列表中所有元素一个个地传递给一个函数,并收集输出。比方说:

+ +
items = [1, 2, 3, 4, 5]
+squared = []
+for i in items:
+    squared.append(i**2)
+
+ +

这里我们使用了一个循环来计算列表中每个元素的平方Map可以让我们用一种简单而漂亮得多的方式来实现。

+ +
items = [1, 2, 3, 4, 5]
+squared = list(map(lambda x: x**2, items)) # map()返回一个迭代器,所以我们需要将它转换为一个列表
+
+ +

不仅用于一列表的输入, 我们甚至可以用于一列表的函数!也即是说,list_of_inputs不仅可以是一个列表,还可以是一个函数列表。比方说:

+ +
def multiply(x):
+    return (x*x)
+def add(x):
+    return (x+x)
+def exponent(x):
+    return (x**x) # x的x次方
+
+funcs = [multiply, add,exponent] # 这里我们有一个函数列表,我们可以将它传递给map,
+for i in range(5):
+    value = list(map(lambda x: x(i), funcs)) # x(i)将会调用funcs中的函数,并传递i作为参数
+    print(list(value))
+
+ +

输出结果为:

+
[0, 0, 1]
+[1, 2, 1]
+[4, 4, 4]
+[9, 6, 27]
+[16, 8, 256]
+
+ +

Filter

+ +

顾名思义,filter过滤列表中的元素,并且返回一个由所有符合要求的元素所构成的列表,符合要求即函数映射到该元素时返回值为True. 这里是一个简短的例子:

+ +
number_list = range(-5, 5)
+less_than_zero = filter(lambda x: x < 0, number_list) # lambda x: x < 0是一个函数,它将会映射到number_list中的每个元素上,符合要求的元素将会被保留
+print(list(less_than_zero))  
+
+

输出结果为:

+
[-5, -4, -3, -2, -1]
+
+

这个filter类似于一个for循环,但它是一个内置函数,并且更快。 +注意:如果map和filter对你来说看起来并不优雅的话,那么你可以看看列表/字典/元组推导式。

+ +

Reduce

+ +

当需要对一个列表进行一些计算并返回结果时,Reduce 是个非常有用的函数。举个例子,当你需要计算一个整数列表的乘积时。

+ +

通常在 python 中你可能会使用基本的 for 循环来完成这个任务。

+ +

现在我们来试试 reduce:

+ +
from functools import reduce # python3中reduce被移到了functools模块中
+product = reduce((lambda x, y: x * y), [1, 2, 3, 4]) # reduce()函数将会对列表中的元素进行迭代计算,x是上一次计算的结果,y是下一个元素,这里我们计算的是1*2*3*4,第一次迭代时x=1,y=2,第二次迭代时x=2,y=3,第三次迭代时x=6,y=4,最后保留的结果为24,是x的最终值。
+print(product)
+
+

输出结果为:

+
24
+
+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/87/a5e13589cd2334bb457120bd4ab7f7410acb6534772cb568ba7a3afa1b1580 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/87/a5e13589cd2334bb457120bd4ab7f7410acb6534772cb568ba7a3afa1b1580 new file mode 100644 index 0000000000..0abb3518ad --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/87/a5e13589cd2334bb457120bd4ab7f7410acb6534772cb568ba7a3afa1b1580 @@ -0,0 +1,121 @@ +I"

定义自己的异常类

+ +

定义自己的错误类型有很多好处,比如可以清楚地显示出潜在的错误,让函数和模块更具可维护性。自定义错误类型还可用来提供额外的调试信息。

+ +

Python中比较常见的泛型异常类包括像 ValueError,但是在栈调试回溯中,返回的内容可能用处不大。

+ +
class NameTooShortError(ValueError):
+	pass
+def validate(name):
+	if len(name) < 10:
+		raise NameTooShortError(name)
+
+ +

引入自定义异常类型来表示名字验证失败,现在有了能够“顾名思义”NameTooShortError 异常类型,它扩展自内置的 ValueError类。(其他的内置Error还包括Exception和TypeError类)

+ +

还可以使用继承来根据逻辑对异常分组,组成层次结构。

+ +

克隆对象

+ +

Python 中的赋值语句不会创建对象的副本,而只是将名称绑定到对象上。对于不可变对象也是如此。但为了处理可变对象或可变对象集合,需要一种方法来创建这些对象的“真实副本”或“克隆体”。 +有时需要用到对象的副本,以便修改副本时不会改动本体。

+ +

浅复制是指构建一个新的容器对象,然后填充原对象中子对象的引用。本质上浅复制只执行 +一层,复制过程不会递归,因此不会创建子对象的副本。 +深复制是递归复制,首先构造一个新的容器对象,然后递归地填充原始对象中子对象的副本。这种方式会遍历整个对象树,以此来创建原对象及其所有子项的完全独立的副本。

+ +

下面的例子中将创建一个新的嵌套列表,然后用 list()工厂函数浅复制:

+ +
xs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
+xs
+ys = list(xs)
+ys
+
+
+ +

这里ys是一个新的独立对象,与xs具有相同的内容:

+ +
>>> xs
+[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
+>>> ys
+[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
+>>>
+
+ +

我们向原对象添加一个新列表,查看ys是否会跟着xs的变化而改变:

+ +
xs.append(['new sublist'])
+xs 
+ys
+
+ +

可以看到,修改浅复制的列表完全不会影响副本。

+ +
>>> xs
+[[1, 2, 3], [4, 5, 6], [7, 8, 9], ['new sublist']]
+>>> ys
+[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
+>>>
+
+ +

但由于前面只创建了原列表的浅副本,所以 ys 仍然含有 xs 子对象的引用!

+ +

这些子对象没有复制,只是在 ys 中再次引用。因此在修改 xs 中的子对象时,这些改动也会反映在 ys 中——因为两个列表共享相同的子对象。这个副本是仅含有一层的浅复制:

+ +
xs[1][0] = 'X'
+xs
+ys
+
+
+ +

这里看上去这是修改了xs,但是ys和xs中索引1处的子列表都被修改了。

+ +
>>> xs
+[[1, 2, 3], ['X', 5, 6], [7, 8, 9], ['new sublist']]
+>>> ys
+[[1, 2, 3], ['X', 5, 6], [7, 8, 9]]
+>>>
+
+ +

而如果在第一步中创建的是 xs 的深副本,那么这两个对象会互相完全独立。

+ +

制作深副本

+ +

使用 copy 模块中定义的 deepcopy()函数创建深副本:

+ +
import copy
+xs = [[1,2,3],[4,5,6],[7,8,9]]
+zs = copy.deepcopy(xs)
+
+ +

这里,它们看起来是一样的。

+ +
>>> xs
+[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
+>>> zs
+[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
+>>>
+
+ +

我们看到如果修改原对象(xs)中的某个子对象,则会发现这些修改不会影响深副本(zs)。

+ +
xs[1][0] = 'X'
+xs
+zs
+
+ +

输出结果如下:

+ +
>>> xs
+[[1, 2, 3], ['X', 5, 6], [7, 8, 9]]
+>>> zs
+[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
+>>>
+
+ +

还可以使用 copy 模块中的一个函数来创建浅副本。copy.copy()函数会创建 +对象的浅副本。

+ +

在代码中,copy.copy()可以清楚地表明这里创建的是浅副本。但对于内置容器,只需要 +使用 list、dict 和 set 这样的工厂函数就能创建浅副本,这种方式更具 Python 特色.

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/89/6e0928f4fa79ec286df727c6dea3bef2849c6956b7dda4085c29204bd87b7d b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/89/6e0928f4fa79ec286df727c6dea3bef2849c6956b7dda4085c29204bd87b7d new file mode 100644 index 0000000000..c9fbf5e616 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/89/6e0928f4fa79ec286df727c6dea3bef2849c6956b7dda4085c29204bd87b7d @@ -0,0 +1,705 @@ +I"T

02 概率论与信息论

+ +

2.1.1 概论与随机变量

+

频率学派概率(frequency probability):概率和事件发生的频率相关。频率是指事件发生的次数与事件总次数的比值。频率学派概率的缺点是,它只能用于离散事件,而不能用于连续事件。因为连续事件的发生次数是无穷的,而事件总次数是有限的,所以它们的比值是无穷小的,这样就无法用频率来表示概率了。因此,频率学派概率只能用于离散事件,而不能用于连续事件。 +贝叶斯学派概率(bayesian probability):概率和事件发生的先验知识相关。也就是说,概率是对某件事情发生的信心程度的度量。每次事件发生,都会更新先验知识,从而得到后验知识。贝叶斯学派概率的缺点是,它需要先验知识,而先验知识往往是不确定的,所以贝叶斯学派概率也是不确定的。 +随机变量(random variable)变量:一个可能随机取不同值的变量,如,抛硬币的结果,掷骰子的结果,抽取的样本的特征等。随机变量的取值是随机的,但是随机变量的取值是有限的,所以随机变量是离散的。

+ +

2.1.2 随机变量的分布

+ +

概论质量函数(probability Mass Function),对于离散型变量,我们先定义一个随机变量,然后用概率质量函数来描述这个随机变量的取值分布。概率质量函数是一个函数,它的输入是随机变量的取值,输出是随机变量取这个值的概率。概率质量函数的定义是,对于离散型随机变量X,它的概率质量函数是一个函数P(x),它的定义域是随机变量X的取值集合。

+ +

如一个离散型x有k个不同的取值,假设x是均匀分布的,那么概率质量函数为:
+\(P(x=x^i)=\frac{1}{k}\)

+ +

概论密度函数(probability Density Function),对于连续型变量,我们先定义一个随机变量,然后用概率密度函数来描述这个随机变量的取值分布。概率密度函数是一个函数,它的输入是随机变量的取值,输出是随机变量取这个值的概率。概率密度函数的定义是,对于连续型随机变量X,它的概率密度函数是一个函数P(x),它的定义域是随机变量X的取值集合。P(x)是一个非负的函数,它的积分是1,即:
+\(\int_{-\infty}^{\infty}P(x)dx=1\) +且满足非负性: +\(\forall x \in \mathbb{X}, P(x)\geq 0\)

+ +

累计分布函数(cumulative distribution function)表示对小于x的概率进行积分,即:
+\(CDF(x)=\int_{-\infty}^{x}P(x)dx\)

+ +
import numpy as np
+import matplotlib.pyplot as plt
+from scipy.stats import uniform
+%matplotlib inline
+
+ +
# generate the sample
+fig,ax = plt.subplots(1,1)
+# 使用参数loc和scale,可以得到[loc, loc + scale]上的均匀分布,size=1000表示样本数,random_state=123表示随机种子
+r = uniform.rvs(loc=0,scale=1,size=1000,random_state=123)
+# density=True表示概率密度,histtype='stepfilled'表示填充,alpha=0.5表示透明度
+ax.hist(r,density=True,histtype='stepfilled',alpha=0.5)
+# 均匀分布pdf
+x = np.linspace(uniform.ppf(0.01),uniform.ppf(0.99),100)
+ax.plot(x,uniform.pdf(x),'r-',lw=5,alpha =0.6,label='uniform pdf')
+
+
+ +
[<matplotlib.lines.Line2D at 0x277a8b4d400>]
+
+ +

png

+ +

2.1.3 条件概论与条件独立

+ +

边缘概论(Marginal Probability)如果我们知道了一组变量的联合概论分布,想要了解其中一个子集的概率分布。这种定义在子集上的概率分布成为边缘概论分布:
+\(\forall x \in \mathbb{X},P(\mathrm{x}=x) = \sum_y P(\mathrm{x}=x,\mathrm{y}=y)\)

+ +

条件概论(conditional probability)在很多情况下,我们不仅关心一个事件的概率,还关心在另一个事件发生的条件下,这个事件的概率。这种在另一个事件发生的条件下,事件的概率称为条件概论。条件概论的定义是:
+\(P(\mathrm{x}=x|\mathrm{y}=y)=\frac{P(\mathrm{x}=x,\mathrm{y}=y)}{P(\mathrm{y}=y)}\) +这个公式表示在y发生的条件下,x发生的概率。

+ +

条件概论的链式法则(chain rule of conditional probability):任何多维随机变量的联合概论分布,都可以分解成只有只有一个变量的条件概论相乘的形式
+\(P(\mathrm{x}=x,\mathrm{y}=y,\mathrm{z}=z)=P(\mathrm{x}=x|\mathrm{y}=y,\mathrm{z}=z)P(\mathrm{y}=y,\mathrm{z}=z)=P(\mathrm{x}=x|\mathrm{y}=y,\mathrm{z}=z)P(\mathrm{y}=y|\mathrm{z}=z)P(\mathrm{z}=z)\)

+ +\[P(x_1,x_2,\cdots,x_n)=P(x_1|x_2,\cdots,x_n)P(x_2|x_3,\cdots,x_n)\cdots P(x_{n-1}|x_n)P(x_n)\] + +

独立性(independence)如果两个随机变量X和Y满足:
+\(P(\mathrm{x}=x,\mathrm{y}=y)=P(\mathrm{x}=x)P(\mathrm{y}=y)\) +也即是联合概论分布可以表示成只有一个变量的条件概论相乘的形式。

+ +

条件独立性(conditional independence)如果关于x和y的条件概率分布对于z的每一个值都满足独立性,那么我们就说x和y在z的条件下是条件独立的。
+\(P(\mathrm{x}=x,\mathrm{y}=y|\mathrm{z}=z)=P(\mathrm{x}=x|\mathrm{z}=z)P(\mathrm{y}=y|\mathrm{z}=z)\)

+ +

2.1.4 随机变量的度量

+

期望(expectation)是随机变量的平均值,期望的定义是:
+\(E(X)=\sum_x xP(X=x)\) +对于连续型随机变量可以通过积分的形式表示:
+\(E(X)=\int_{-\infty}^{\infty}xP(X=x)dx\)

+ +

另外,期望是线性的,即:
+\(E(aX+bY)=aE(X)+bE(Y)\)

+ +

方差(variance)是随机变量的离散程度,方差的定义是:
+\(Var(X)=E[(X-E(X))^2]\)

+ +

标准差(standard deviation)是方差的平方根,标准差的定义是:
+\(\sigma(X)=\sqrt{Var(X)}\)

+ +

协方差(covariance)是两个随机变量的线性相关程度,协方差的定义是:
+\(Cov(X,Y)=E[(X-E(X))(Y-E(Y))]\)

+ +

注意协方差的符号表示两个随机变量的线性相关程度,正数表示正相关,负数表示负相关,0表示不相关。
+注意独立与零协方差更强,因为独立还排除了非线性的相关。

+ +
x= np.array([1,2,3,4,5,6,7,8,9,10])
+y = np.array([10,9,8,7,6,5,4,3,2,1])
+Mean = np.mean(x)
+Var = np.var(x)
+Var_unbiased = np.var(x,ddof=1)# ddof=1表示无偏估计
+Std = np.std(x)
+Cov = np.cov(x,y)
+Mean,Var,Var_unbiased,Std,Cov
+
+ +
(5.5,
+ 8.25,
+ 9.166666666666666,
+ 2.8722813232690143,
+ array([[ 9.16666667, -9.16666667],
+        [-9.16666667,  9.16666667]]))
+
+ +

2.1.5 常用概率分布

+

伯努利分布(Bernoulli distribution)是一个二项分布的特例,它只有两个可能的取值,0和1,它的概率质量函数是(表示一次实验成功的概率):
+\(P(X=1)=p\), +\(P(X=0)=1-p\), +\(P(X=x)=p^x(1-p)^{1-x}\)

+ +
def plot_distribution(X,axes=None):
+    '''给定随机变量X,绘制其PDF,PMF,CDF'''
+    if axes is None:
+        fig,axes = plt.subplots(1,3,figsize=(12,4))
+    x_min,x_max = X.interval(0.99)# 99%的概率密度在区间内
+    x = np.linspace(x_min,x_max,1000)
+    # 连续型变量,画PDF;离散型变量,画PMF
+    if hasattr(X.dist,'pdf'):
+        # axes[0]表示第一行,第一列
+        axes[0].plot(x,X.pdf(x),label='PDF')
+        axes[0].fill_between(x,X.pdf(x),alpha=0.5) # 填充,透明度0.5
+    else:
+        x_int = np.unique(x.astype(int))# 取整,unique去重
+        axes[0].bar(x_int,X.pmf(x_int),label='PMF') 
+    # CDF
+    axes[1].plot(x,X.cdf(x),label='CDF')
+    for ax in axes:
+        ax.legend()
+    return axes
+
+ +
from scipy.stats import bernoulli
+fig,axes = plt.subplots(1,2,figsize=(12,4))
+r = bernoulli(p=0.6) # 生成伯努利分布
+plot_distribution(r,axes)
+
+ +
array([<matplotlib.axes._subplots.AxesSubplot object at 0x00000277A9C8B8E0>,
+       <matplotlib.axes._subplots.AxesSubplot object at 0x00000277A9CB3100>],
+      dtype=object)
+
+ +

png

+ +
# 生成实验成功的次数
+p = 0.6
+def trials(n_samples):
+    # 二项分布,n_samples表示实验次数,p表示成功的概率,samples表示成功的次数
+    samples = np.random.binomial(n_samples,p) 
+    proba_zero = (n_samples-samples)/n_samples# 0次成功的概率
+    proba_one = samples/n_samples# 1次成功的概率
+    return [proba_zero,proba_one]
+fig,axes = plt.subplots(1,2,figsize=(12,4))
+# 一次实验,伯努利分布
+n_samples = 1
+axes[0].bar([0,1],trials(n_samples),label ='Bernoulli')
+axes[0].set_title('n_samples = {}'.format(n_samples))
+# n次实验,二项分布
+n_samples = 1000
+axes[1].bar([0,1],trials(n_samples),label ='Binomial')
+axes[1].set_title('n_samples = {}'.format(n_samples))
+for ax in axes:
+    ax.legend()
+
+ +

png

+ +

范畴分布(Categorical distribution,分类分布)是一个多项分布的特例,它的概率质量函数是(表示第k类的概率):
+\(P(X=k)=p_k\)

+ +

例如每次试验的结果就可以表示成一个k维的向量,每个维度表示一种结果,只有此次试验的结果对应的维度为1,其余维度为0,例如:
+\(P(X=(1,0,0,0))=p_1\)
+\(P(X=(0,1,0,0))=p_2\)
+\(P(X=(0,0,1,0))=p_3\)
+\(P(X=(0,0,0,1))=p_4\)

+ +
def k_possibilities(k):
+    '''生成k个可能的结果'''
+    res = np.random.rand(k)# random.rand(k)生成k个[0,1)之间的随机数
+    _sum = sum(res) # 求和
+    for i,x in enumerate(res):
+        res[i] = x/_sum # 归一化,enumerate返回索引和值,i表示索引,x表示值
+    return res
+fig,axes = plt.subplots(1,2,figsize=(12,4))
+# 一次实验,范畴分布
+n_samples = 1
+k=10
+samples = np.random.multinomial(n_samples,k_possibilities(k))
+axes[0].bar(range(len(samples)),samples/n_samples,label ='Multinomial')
+
+# n次实验,多项分布
+n_samples = 1000
+samples = np.random.multinomial(n_samples,k_possibilities(k))
+axes[1].bar(range(len(samples)),samples/n_samples,label ='Multinomial')
+for ax in axes:
+    ax.legend()
+
+ +

png

+ +

高斯分布(Gaussian distribution,正态分布)是一个连续型概率分布,若随机变量$X\sim \mathcal{N}(\mu,\sigma^2)$,它的概率密度函数是:
+\(P(X=x)=\frac{1}{\sqrt{2\pi\sigma^2}}e^{-\frac{(x-\mu)^2}{2\sigma^2}}\)
+有时也会用$\beta = \frac{1}{\sigma^2}$来表示分布的精度(精度越大,分布越狭窄,precision):
+\(P(X=x)=\frac{1}{\sqrt{2\pi\beta^{-1}}}e^{-\frac{(x-\mu)^2}{2\beta}}\)

+ +

中心极限定理(central limit theorem)是一个重要的定理,它表示当样本量足够大时,样本均值的分布会趋近于正态分布,如噪声的分布就是一个典型的例子,噪声的分布是均匀分布,但是当噪声的数量足够多时,噪声的均值的分布就会趋近于正态分布。

+ +

如果我们对随机变量进行标准化,即将随机变量减去均值并除以标准差,那么标准化后的随机变量的均值为0,标准差为1,这样的随机变量称为标准正态分布(standard normal distribution),
+\(Z=\frac{X-\mu}{\sigma}\)

+ +
from scipy.stats import norm
+fig,axes = plt.subplots(1,2,figsize= (12,4))
+mu,sigma = 0,1# 均值,标准差
+r = norm(loc=mu,scale=sigma)# 生成正态分布
+plot_distribution(r,axes) #绘制r的PDF和CDF
+
+ +
array([<matplotlib.axes._subplots.AxesSubplot object at 0x000001B387029910>,
+       <matplotlib.axes._subplots.AxesSubplot object at 0x000001B386E8E430>],
+      dtype=object)
+
+ +

png

+ +

多元高斯分布(multivariate Gaussian distribution)是一个多维的高斯分布,它的概率密度函数是:
+\(P(X=x;\sigma,\Sigma)=\frac{1}{(2\pi)^{n/2}|\Sigma|^{1/2}}e^{-\frac{1}{2}(x-\mu)^T\Sigma^{-1}(x-\mu)}\) +
+具体推导过程如下:
+先假设 $\mathrm{n}$ 个变量 $x=\left[x_1, x_2, \cdots, x_n\right]^{\mathrm{T}}$ 互不相关,且服从正态分布 (维度不相关多元正态分布),各个维度的均值 $E(x)=\left[\mu_1, \mu_2, \cdots, \mu_n\right]^{\mathrm{T}}$ ,方差 $\sigma(x)=\left[\sigma_1, \sigma_2, \cdots, \sigma_n\right]^{\mathrm{T}}$ +
+根据联合概率密度公式: +\(\begin{aligned} +& f(x)=p\left(x_1, x_2 \ldots x_n\right)=p\left(x_1\right) p\left(x_2\right) \ldots p\left(x_n\right)=\frac{1}{(\sqrt{2 \pi})^n \sigma_1 \sigma_2 \cdots \sigma_n} e^{-\frac{\left(x_1-\mu_1\right)^2}{2 \sigma_1^2}-\frac{\left(x_2-\mu_2\right)^2}{2 \sigma_2^2} \cdots-\frac{\left(x_n-\mu_n\right)^2}{2 \sigma_n}} \\ +& \text { 令 } z^2=\frac{\left(x_1-\mu_1\right)^2}{\sigma_1^2}+\frac{\left(x_2-\mu_2\right)^2}{\sigma_2^2} \cdots+\frac{\left(x_n-\mu_n\right)^2}{\sigma_n^2}, \sigma_z=\sigma_1 \sigma_2 \cdots \sigma_n +\end{aligned}\) +这样多元正态分布又可以写成一元那种漂亮的形式了(注意一元与多元的差别):
+\(f(z)=\frac{1}{(\sqrt{2 \pi})^n \sigma_z} e^{-\frac{z^2}{2}}\) +
+因为多元正态分布有着很强的几何思想,单纯从代数的角度看待z很难看出 z的概率分布规律,这里需要转换成矩阵形式:
+\(z^2=z^{\mathrm{T}} z=\left[x_1-\mu_1, x_2-\mu_2, \cdots, x_n-\mu_n\right]\left[\begin{array}{cccc} +\frac{1}{\sigma_1^2} & 0 & \cdots & 0 \\ +0 & \frac{1}{\sigma_2^2} & \cdots & 0 \\ +\vdots & \cdots & \cdots & \vdots \\ +0 & 0 & \cdots & \frac{1}{\sigma_n^2} +\end{array}\right]\left[x_1-\mu_1, x_2-\mu_2, \cdots, x_n-\mu_n\right]^{\mathrm{T}}\) +
+等式比较长,让我们要做一下变量替换:
+\(x-\mu_x=\left[x_1-\mu_1, x_2-\mu_2, \cdots, x_n-\mu_n\right]^{\mathrm{T}}\) +
+定义一个符号 +\(\sum=\left[\begin{array}{cccc} +\sigma_1^2 & 0 & \cdots & 0 \\ +0 & \sigma_2^2 & \cdots & 0 \\ +\vdots & \cdots & \cdots & \vdots \\ +0 & 0 & \cdots & \sigma_n^2 +\end{array}\right]\) +
+$\sum$ 代表变量 $\mathrm{X}$ 的协方差矩阵, i行j列的元素值表示 $x_i$ 与 $x_j$ 的协方差 +因为现在变量之间是相互独立的,所以只有对角线上 $(i=j)$ 存在元素,其他地方都等于 0 ,且 $x_i$ 与它本身的协方差就等于方差。 +
+
+$\sum$ 是一个对角阵,根据对角矩阵的性质,它的逆矩阵:
+\(\left(\sum\right)^{-1}=\left[\begin{array}{cccc} +\frac{1}{\sigma_1^2} & 0 & \cdots & 0 \\ +0 & \frac{1}{\sigma_2^2} & \cdots & 0 \\ +\vdots & \cdots & \cdots & \vdots \\ +0 & 0 & \cdots & \frac{1}{\sigma_n^2} +\end{array}\right].\) +
+对角矩阵的行列式 $=$ 对角元素的乘积 +\(\sigma_z=\left|\sum\right|^{\frac{1}{2}}=\sigma_1 \sigma_2 \ldots \sigma_n\) +
+替换变量之后,等式可以简化为: +\(z^{\mathrm{T}} z=\left(x-\mu_x\right)^{\mathrm{T}} \sum^{-1}\left(x-\mu_x\right)\) +
+代入以z为自变量的标准高斯分布函数中: +\(f(z)=\frac{1}{(\sqrt{2 \pi})^n \sigma_z} e^{-\frac{z^2}{2}}=\frac{1}{(\sqrt{2 \pi})^n \sum^{\frac{1}{2}}} e^{-\frac{\left(x-\mu_x\right)^T(\Gamma)^{-1}\left(x-\mu_x\right)}{2}}\) +
+注意前面的系数变化: 从非标准正态分布 $->$ 标准正态分布需要将概率密度函数的高度压缩 $\left|\sum\right|^{\frac{1}{2}}$ 倍,从一维 $\rightarrow$ n维的过 程中,每增加一维,高度将压缩 $\sqrt{2 \pi}$ 倍

+ +

reference: +https://www.cnblogs.com/bingjianing/p/9117330.html

+ +
from scipy.stats import multivariate_normal
+x,y = np.mgrid[-1:1:.01,-1:1:.01]# 生成网格点,步长为0.01
+pos = np.dstack((x,y))# 生成网格点坐标,shape=(200,200,2)
+sigma = [[2,0.3],[0.3,0.5]] # 协方差矩阵
+mu = [0.5,0.2]
+X = multivariate_normal(mu,sigma)# 生成多元正态分布
+fig,axes = plt.subplots(1,1,figsize=(12,4)) 
+axes.contourf(x,y,X.pdf(pos))# 绘制等高线,pos表示在网格点上的概率密度
+
+ +
<matplotlib.contour.QuadContourSet at 0x1b386e185b0>
+
+ +

png

+ +

指数分布(Exponential distribution)是描述泊松过程中的事件之间的时间的概率分布,即事件以恒定平均速率连续且独立地发生的过程。 这是伽马分布的一个特殊情况。 它是几何分布的连续模拟,它具有无记忆(Memoryless Property)的关键性质,也即是$P(T>t+s|T>t)=P(T>s)$。 +
+除了用于分析泊松过程外,还可以在其他各种环境中找到,其概率密度函数为: +
+\(p(x)=\left\{\begin{array}{c} +\frac{1}{\theta} e^{-\frac{x}{\theta}}, x>0 \\ +0, x \leq 0 +\end{array} \quad(\theta>0)\right.\) +
+则称随机变量$X$ 服从参数为 $\theta$ 的指数分布, 记为 $X \sim \operatorname{EXP}(\theta)$. +
+其中 $\lambda = \frac{1}{\theta}$ 称为率参数(rate parameter)。 +
+即每单位时间内发生某事件的次数(比方说:如果你平均每个小时接到2次电话,那么你预期等待每一次电话的时间是半个小时)。指数分布的期望和方差分别为 $\theta$ 和 $\theta^2$.

+ +

指数分布的累计分布函数为:
+\(P(X \leq x) = 1 - e^{-\frac{x}{\theta}}\)

+ +
from scipy.stats import expon
+fig,axes = plt.subplots(1,2,figsize=(12,4))
+r = expon(scale=2)# 生成指数分布,scale=2表示λ=1/2
+plot_distribution(r,axes)
+
+
+ +
array([<matplotlib.axes._subplots.AxesSubplot object at 0x000001B387102DF0>,
+       <matplotlib.axes._subplots.AxesSubplot object at 0x000001B387165340>],
+      dtype=object)
+
+ +

png

+ +

拉普拉斯分布(Laplace distribution)是一种连续概率分布,它是双指数分布的特殊情况。它是一种对称分布,其概率密度函数为:
+\(p(x) = \frac{1}{2 \lambda} e^{ - \frac{|x - \mu|}{\lambda}}\)

+ +

其中 $\mu$ 称为分布的位置参数(location parameter),$\lambda$ 称为分布的尺度参数(scale parameter)。拉普拉斯分布的期望和方差分别为 $\mu$ 和 $2 \lambda^2$.

+ +
from scipy.stats import laplace
+fig,axes = plt.subplots(1,2,figsize=(12,4))
+mu,gamma = 0,1 # 均值,标准差
+r = laplace(loc=0,scale=1)# 生成拉普拉斯分布
+plot_distribution(r,axes)
+
+ +
array([<matplotlib.axes._subplots.AxesSubplot object at 0x000001B3870DF130>,
+       <matplotlib.axes._subplots.AxesSubplot object at 0x000001B385D4DE20>],
+      dtype=object)
+
+ +

png

+ +

Dirac 分布:Dirac delta function,也称为 Dirac 分布。

+ +

Dirichlet分布 +基础知识:conjugate priors共轭先验 +共轭先验是指这样一种概率密度:它使得后验概率的密度函数与先验概率的密度函数具有相同的函数形式。它极大地简化了贝叶斯分析。 +如何解释这句话。由于$P(u|D) = p(D|u)p(u)/p(D)$,其中D是给定的一个样本集合,因此对其来说p(D)是一个确定的值,可以理解为一个常数。P(u|D)是后验概率—-即观察到一系列样本数据后模型参数服从的概率,p(D|u)是似然概率—-在给定的模型参数u下样本数据服从这一概率模型的相似程度,p(u)是u的先验概率—-在我们一无所知的情况下u的概率分布。P(u|D)的函数形式完全由p(D|u)和p(u)的乘积决定。如果p(u)的取值使p(u|D)和p(D|u)相同的表达形式(关于u的表达形式),就称p(u)为共轭先验。

+ +

reference:
+机器学习的数学基础(1)–Dirichlet分布

+ +

Beta分布式二项分布的共轭先验分布.

+ +

Dirichlet分布是一个多维的概率分布,它是一组(多元)独立的 $\operatorname{Beta}$ 分布的共轭先验分布。在贝叶斯推断(Bayesian inference)中,狄利克雷分布作为多项分布的共轭先验得到应用,在机器学习(machine learning)中被用于构建狄利克雷混合模型(Dirichlet mixture model)是一类在实数域以正单纯形(standard simplex)为 支撑集(support)的高维连续概率分布,是Beta分布在高维情形的推广。

+ +

reference:
+理解Gamma分布、Beta分布与Dirichlet分布 + +
+最通俗易懂的白话狄利克雷过程(Dirichlet Process)

+ +

2.1.6 常见函数的有用性质

+ +

logisitc sigmoid函数通常用来产生伯努利分布的概率,因为它的范围是 (0, 1),因此它的导数是伯努利分布的概率密度函数。sigmoid 函数在变量取绝对值⾮常⼤的正值或负值时会出现饱和 (Saturate)现象,意味着函数会变得很平,并且对输⼊的微⼩改变会变得不敏感。
+\(sigmoid(x) = \frac{1}{1+e^{-x}}\)

+ +

softplus函数
+\(softplus(x) = \log(1+e^x)\)

+ +

因为它的范围是 (0, ∞),softplus函数可以用来产生正太分布的$\beta$和$\sigma$参数,因此它的导数是正太分布的概率密度函数。 +softplus函数名称来源于它是一个平滑的ReLU函数,ReLU函数是一个非线性函数,它的定义是 +\(ReLU(x) = max(0, x)\)

+ +
x = np.linspace(-10,10,1000)
+sigmoid = 1/(1+np.exp(-x))
+softplus = np.log(1+np.exp(x))
+fig,axes = plt.subplots(1,2,figsize=(12,4))
+axes[0].plot(x,sigmoid)
+axes[0].set_title('Sigmoid')
+axes[1].plot(x,softplus)
+axes[1].set_title('Softplus')
+
+
+ +
Text(0.5, 1.0, 'Softplus')
+
+ +

png

+ +

2.2 信息论

+

信息论背后的思想:一件不太可能的事件比一件比较可能的事件更有信息量。(信息量的度量就等于不确定性的多少。)
+信息 (Information) 需要满⾜的三个条件:

+ + +

自信息 (Self-information) 是信息论中的基本概念,它是指单个事件发⽣的信息量。自信息的定义是
+\(I(x) = -\log P(x)\)

+ +

香农熵 (Shannon Entropy)是对于整个概论分布P,随机变量的不确定性的衡量:
+\(H(P) = -( p_1 * \log p_1 + p_2 * \log p_2 + \cdots + p_n * \log p_n) =-\sum_{x}P(x)\log P(x)\)

+ +

联合熵 (Joint Entropy) 是对于两个随机变量X和Y,它们的联合分布P(X, Y),随机变量的不确定性的衡量:
+\(H(X, Y) = -\sum_{x,y}P(x, y)\log P(x, y)\)

+ +

条件熵 (Conditional Entropy) 是对于两个随机变量X和Y,它们的联合分布P(X, Y),随机变量的不确定性的衡量:
+\(H(Y|X) = -\sum_{x,y}P(x, y)\log P(y|x)\)

+ +

互信息 (Mutual Information) 表示两个信息相交的部分:
+\(I(X;Y) = H(X)+H(Y) - H(X,Y)\)

+ +

信息变差(Variation of Information) 表示两个事件的信息不想交的部分:
+\(VI(X;Y) = H(X)+H(Y) - 2I(X;Y)\)

+ +
p = np.linspace(1e-6,1-1e-6,1000) # 生成概率
+entropy = -p*np.log(p)-(1-p)*np.log(1-p) # 香农熵 (Shannon Entropy)
+plt.figure(figsize=(4,4))
+plt.plot(p,entropy)
+plt.title('Shannon Entropy')
+
+
+ +
Text(0.5, 1.0, 'Shannon Entropy')
+
+ +

png

+ +

信息增益(Kullback–Leibler divergence)又称information divergence,information gain,relative entropy 或者KLIC。 +在概率论和信息论中,信息增益是非对称的,用以度量两种概率分布P和Q的差异。
+\(D_{KL}(P||Q) = \sum_{x}P(x)\log \frac{P(x)}{Q(x)}\)

+ +

交叉熵 (Cross Entropy) 假设P是真实的分布,Q是预测的分布,那么交叉熵就是真实分布和预测分布之间的距离:
+\(H(P, Q) = H(P) + D_{KL}(P||Q) = -\sum_{x}P(x)\log Q(x)\)

+ +
# D_{KL}(P||Q)  与D_{KL}(Q||P)比较
+x  = np.linspace(1,8,500)
+y1 = norm.pdf(x,loc =3,scale = 0.5) # loc=3,scale=0.5表示均值为3,标准差为0.5
+y2 = norm.pdf(x,loc =6,scale = 0.5) 
+p = y1+y2 #构造p(x)
+KL_pq ,KL_qp = [],[]
+q_list = []
+for mu in np.linspace(0,10,50):
+    for sigma in np.linspace(0.1,1,50): # 生成50*50个q(x),寻找最优q(x)
+        q = norm.pdf(x,loc =mu,scale = sigma) # 构造q(x)
+        q_list.append(q)
+        KL_pq.append(np.sum(p*np.log(p/q))) # D_{KL}(P||Q)
+        KL_qp.append(np.sum(q*np.log(q/p))) # D_{KL}(Q||P)
+# min
+min_KL_pq = np.argmin(KL_pq) # 最小值的索引
+min_KL_qp = np.argmin(KL_qp)
+
+fig,axes = plt.subplots(1,2,figsize=(12,4))
+axes[0].set_ylim(0,0.8)
+axes[0].plot(x,p/2,'b',label='p(x)')
+axes[0].plot(x,q_list[min_KL_pq],'r',label='$q^*(x)$')
+axes[0].set_title('min D_{KL}(P||Q) ')
+axes[0].set_xlabel('x')
+axes[0].set_ylabel('p(x)')
+
+axes[1].set_ylim(0,0.8)
+axes[1].plot(x,p/2,'b',label='p(x)')
+axes[1].plot(x,q_list[min_KL_qp],'r',label='$q^*(x)$')
+axes[1].set_title('min D_{KL}(Q||P)')
+axes[1].set_xlabel('x')
+axes[1].set_ylabel('p(x)')
+
+ +
<ipython-input-60-0ceffff0da69>:12: RuntimeWarning: divide by zero encountered in true_divide
+  KL_pq.append(np.sum(p*np.log(p/q))) # D_{KL}(P||Q)
+<ipython-input-60-0ceffff0da69>:12: RuntimeWarning: overflow encountered in true_divide
+  KL_pq.append(np.sum(p*np.log(p/q))) # D_{KL}(P||Q)
+<ipython-input-60-0ceffff0da69>:13: RuntimeWarning: divide by zero encountered in log
+  KL_qp.append(np.sum(q*np.log(q/p))) # D_{KL}(Q||P)
+<ipython-input-60-0ceffff0da69>:13: RuntimeWarning: invalid value encountered in multiply
+  KL_qp.append(np.sum(q*np.log(q/p))) # D_{KL}(Q||P)
+
+
+
+
+
+Text(0, 0.5, 'p(x)')
+
+ +

png

+ +

信息增益 (Information Gain) 是指在知道特征X的条件下,特征Y的不确定性减少的程度:
+\(IG(X;Y) = H(Y) - H(Y|X)\)

+ +
import numpy as np
+import pandas as pd
+from math import log
+ 
+def create_data():
+    datasets = [['青年', '否', '否', '一般', '否'],
+                ['青年', '否', '否', '好', '否'],
+                ['青年', '是', '否', '好', '是'],
+                ['青年', '是', '是', '一般', '是'],
+                ['青年', '否', '否', '一般', '否'],
+                ['中年', '否', '否', '一般', '否'],
+                ['中年', '否', '否', '好', '否'],
+                ['中年', '是', '是', '好', '是'],
+                ['中年', '否', '是', '非常好', '是'],
+                ['中年', '否', '是', '非常好', '是'],
+                ['老年', '否', '是', '非常好', '是'],
+                ['老年', '否', '是', '好', '是'],
+                ['老年', '是', '否', '好', '是'],
+                ['老年', '是', '否', '非常好', '是'],
+                ['老年', '否', '否', '一般', '否'],
+                ]
+    labels = [u'年龄', u'有工作', u'有自己的房子', u'信贷情况', u'类别']
+    # 返回数据集和每个维度的名称
+    return datasets, labels
+ 
+ 
+# 熵
+def calc_ent(datasets):
+    data_length = len(datasets)
+    label_count = {}
+    for i in range(data_length):
+        label = datasets[i][-1]
+        if label not in label_count:
+            label_count[label] = 0
+        label_count[label] += 1
+    ent = -sum([(p / data_length) * log(p / data_length, 2) for p in label_count.values()])
+    return ent
+ 
+ 
+# 经验条件熵
+def cond_ent(datasets, axis=0):
+    data_length = len(datasets)
+    feature_sets = {}
+    for i in range(data_length):
+        feature = datasets[i][axis]
+        if feature not in feature_sets:
+            feature_sets[feature] = []
+        feature_sets[feature].append(datasets[i])
+    cond_ent = sum([(len(p) / data_length) * calc_ent(p) for p in feature_sets.values()])
+    return cond_ent
+ 
+ 
+# 信息增益
+def info_gain(ent, cond_ent):
+    return ent - cond_ent
+ 
+ 
+def info_gain_train(datasets):
+    count = len(datasets[0]) - 1
+    ent = calc_ent(datasets)
+    best_feature = []
+    for c in range(count):
+        c_info_gain = info_gain(ent, cond_ent(datasets, axis=c))
+        best_feature.append((c, c_info_gain))
+        print('特征({}) - info_gain - {:.3f}'.format(labels[c], c_info_gain))
+    # 比较大小
+    best_ = max(best_feature, key=lambda x: x[-1])
+    return '特征({})的信息增益最大,选择为根节点特征'.format(labels[best_[0]])
+ 
+datasets, labels = create_data()
+train_data = pd.DataFrame(datasets, columns=labels)
+print(train_data)
+print('特征信息增益为:', info_gain_train(np.array(datasets)))
+
+ +
    年龄 有工作 有自己的房子 信贷情况 类别
+0   青年   否      否   一般  否
+1   青年   否      否    好  否
+2   青年   是      否    好  是
+3   青年   是      是   一般  是
+4   青年   否      否   一般  否
+5   中年   否      否   一般  否
+6   中年   否      否    好  否
+7   中年   是      是    好  是
+8   中年   否      是  非常好  是
+9   中年   否      是  非常好  是
+10  老年   否      是  非常好  是
+11  老年   否      是    好  是
+12  老年   是      否    好  是
+13  老年   是      否  非常好  是
+14  老年   否      否   一般  否
+特征(年龄) - info_gain - 0.083
+特征(有工作) - info_gain - 0.324
+特征(有自己的房子) - info_gain - 0.420
+特征(信贷情况) - info_gain - 0.363
+特征信息增益为: 特征(有自己的房子)的信息增益最大,选择为根节点特征
+
+ +

2.3 图模型

+

图模型(Graphical Model)是一种表示概率分布的方法,它将概率分布表示为一个图,图中的节点表示随机变量,节点之间的边表示随机变量之间的依赖关系。

+ +

2.3.1 有向图模型 directed graphical model

+ +

有向图模型是指图中的边是有向的,即边的方向表示了随机变量之间的依赖关系。有向图模型可以表示条件概率分布(CPD),也可以表示马尔科夫随机场。

+ +

有向图的代表是贝叶斯网。 +贝叶斯⽹与朴素贝叶斯模型建⽴在相同的直观假设上:通过利用分布的条件独立性来获得紧凑而自然的表示。贝叶斯⽹核⼼是⼀个有向⽆环图(DAG),其节点为论域中的随机变量,节点间的有向箭头表⽰这两个节点的依赖关系。

+ +

有向图示例

+ +

有向⽆环图可以由如下 3 种元结构构成:

+ + +

贝叶斯网的独立性

+ + +
import networkx as nx
+from pgmpy.models import BayesianModel
+from pgmpy.factors.discrete import TabularCPD
+import matplotlib.pyplot as plt
+%matplotlib inline
+
+# 建立一个简单贝叶斯模型框架
+model = BayesianModel([('a', 'b'), ('a', 'c'), ('b', 'c'), ('b', 'd'), ('c', 'e')])
+# 最顶层的父节点的概率分布表
+cpd_a = TabularCPD(variable='a', variable_card=2, values=[[0.6, 0.4]]) # a: (0,1)
+
+# 其它各节点的条件概率分布表(行对应当前节点索引,列对应父节点索引)
+cpd_b = TabularCPD(variable='b', variable_card=2, # b: (0,1)
+values=[[0.75, 0.1],
+[0.25, 0.9]],
+evidence=['a'],
+evidence_card=[2])
+cpd_c = TabularCPD(variable='c', variable_card=3, # c: (0,1,2)
+values=[[0.3, 0.05, 0.9, 0.5],
+[0.4, 0.25, 0.08, 0.3],
+[0.3, 0.7, 0.02, 0.2]],
+evidence=['a', 'b'],
+evidence_card=[2, 2])
+cpd_d = TabularCPD(variable='d', variable_card=2, # d: (0,1)
+values=[[0.95, 0.2],
+[0.05, 0.8]],
+evidence=['b'],
+evidence_card=[2])
+cpd_e = TabularCPD(variable='e', variable_card=2, # e: (0,1)
+values=[[0.1, 0.4, 0.99],
+[0.9, 0.6, 0.01]],
+evidence=['c'],
+evidence_card=[3])
+
+# 将各节点的概率分布表加入网络
+model.add_cpds(cpd_a, cpd_b, cpd_c, cpd_d, cpd_e)
+# 验证模型数据的正确性
+print(u"验证模型数据的正确性:",model.check_model())
+# 绘制贝叶斯图 (节点 + 依赖关系)
+nx.draw(model, with_labels=True, node_size=1000, font_weight='bold', node_color='y', \
+pos={"e":[4,3],"c":[4,5],"d":[8,5],"a":[2,7],"b":[6,7]})
+plt.text(2,7,model.get_cpds("a"), fontsize=10, color='b')
+plt.text(5,6,model.get_cpds("b"), fontsize=10, color='b')
+plt.text(1,4,model.get_cpds("c"), fontsize=10, color='b')
+plt.text(4.2,2,model.get_cpds("e"), fontsize=10, color='b')
+plt.text(7,3.4,model.get_cpds("d"), fontsize=10, color='b')
+
+plt.show()
+
+ +
---------------------------------------------------------------------------
+
+ModuleNotFoundError                       Traceback (most recent call last)
+
+<ipython-input-1-96c0e1595c11> in <module>
+      1 import networkx as nx
+----> 2 from pgmpy.models import BayesianModel
+      3 from pgmpy.factors.discrete import TabularCPD
+      4 import matplotlib.pyplot as plt
+      5 get_ipython().run_line_magic('matplotlib', 'inline')
+
+
+ModuleNotFoundError: No module named 'pgmpy'
+
+ +

2.3.2 无向图模型 (Undirected Model)

+

马尔可夫网节点间的依赖关系是无向的(相互平等的关系),⽆法⽤条件概率分布来表⽰,为此为引⼊极大团概念,进⽽为每个极⼤团引⼊⼀个势函数作为因⼦,然后将联合概率分布表⽰成这些因⼦的乘积再归⼀化,归⼀化常数被称作配分函数。

+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/8a/3c35c98b80d23a92f5607416c89d74b4e2f6df6a65a01ae9fbc5f0a97f9370 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/8a/3c35c98b80d23a92f5607416c89d74b4e2f6df6a65a01ae9fbc5f0a97f9370 new file mode 100644 index 0000000000..ac6da5c2fb --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/8a/3c35c98b80d23a92f5607416c89d74b4e2f6df6a65a01ae9fbc5f0a97f9370 @@ -0,0 +1,10 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/8a/cb3939c95b34248ff340d740e1710ffe248f6bb0bf665d1a7ddff0c8ba173b b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/8a/cb3939c95b34248ff340d740e1710ffe248f6bb0bf665d1a7ddff0c8ba173b new file mode 100644 index 0000000000..cf38d3e862 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/8a/cb3939c95b34248ff340d740e1710ffe248f6bb0bf665d1a7ddff0c8ba173b @@ -0,0 +1,16 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/8d/5d61d223788af1226aa03f3307b97ae69ed96673ec2e9086d6602fd5a814e0 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/8d/5d61d223788af1226aa03f3307b97ae69ed96673ec2e9086d6602fd5a814e0 new file mode 100644 index 0000000000..ac12ff5c75 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/8d/5d61d223788af1226aa03f3307b97ae69ed96673ec2e9086d6602fd5a814e0 @@ -0,0 +1,2 @@ +I"\

1. 随机生成一个随机的string

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/8d/b8a20d229e46ab19f3807a83d3723d880b68a4bb88084a2155363a52035a7e b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/8d/b8a20d229e46ab19f3807a83d3723d880b68a4bb88084a2155363a52035a7e new file mode 100644 index 0000000000..f166054b5f --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/8d/b8a20d229e46ab19f3807a83d3723d880b68a4bb88084a2155363a52035a7e @@ -0,0 +1,13 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/90/013f2eb8f0b885d1fed67948900fb41787f6704b94dd0bb3aaa04b4cc4fca2 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/90/013f2eb8f0b885d1fed67948900fb41787f6704b94dd0bb3aaa04b4cc4fca2 new file mode 100644 index 0000000000..c934c5cf2a --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/90/013f2eb8f0b885d1fed67948900fb41787f6704b94dd0bb3aaa04b4cc4fca2 @@ -0,0 +1,6 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/90/56943a71b88e2eefd911fec807a6d15382d681283cab5f62b8de33fd3f6970 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/90/56943a71b88e2eefd911fec807a6d15382d681283cab5f62b8de33fd3f6970 new file mode 100644 index 0000000000..52c8fbd5a8 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/90/56943a71b88e2eefd911fec807a6d15382d681283cab5f62b8de33fd3f6970 @@ -0,0 +1,5 @@ +I"

00 写在前面

+

如何将ipynb文件转为markdown文件?

+
jupyter nbconvert --to markdown note.ipynb
+
+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/91/51c77c1f9ab47b4721f0eb597c21abc53166e7d5996cda3bba0c5c217dcda0 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/91/51c77c1f9ab47b4721f0eb597c21abc53166e7d5996cda3bba0c5c217dcda0 new file mode 100644 index 0000000000..f344862976 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/91/51c77c1f9ab47b4721f0eb597c21abc53166e7d5996cda3bba0c5c217dcda0 @@ -0,0 +1,356 @@ +I"mB

1. 基本命令

+ +

top

+ +

du

+ +

查看/proc目录下的系统各种信息

+ +

/proc 是一个虚拟文件系统,它提供了一种机制,通过读取和写入特定的文件或目录,可以访问内核和系统信息。

+ +
# 命令行数太多可以使用more命令或者grep命令进行筛选
+# 查看CPU信息
+cat /proc/cpuinfo
+# 会输出包含 "model name" 字符串的行,即 CPU 型号的信息
+cat /proc/cpuinfo | grep "model name"
+# 只查看 CPU 核心数,可以使用以下命令:
+cat /proc/cpuinfo | grep "cpu cores"
+
+# 查看内存参数
+cat /proc/meminfo
+# 查看总内存
+cat /proc/meminfo | grep "MemTotal"
+# 查看系统空闲内存大小
+cat /proc/meminfo | grep "MemFree"
+
+# 系统的各种参数和设置,如内核参数、网络设置、文件系统设置等
+# abi  debug  dev  fs  kernel  net  user  vm
+cat /proc/sys/* 
+# 网络信息,如 TCP/IP 连接、网络接口、路由表等
+# 包括tcp、tcp6、udp、udp6等
+cat /proc/net/*
+# 查看tcp连接的各种信息
+cat /proc/net/tcp
+
+# 指定进程的信息,如进程状态、内存占用、打开的文件等(进程资源使用情况)
+cd /proc/pidnumber
+
+ +

top:查看系统的进程信息,包括 CPU 占用率、内存占用率等。

+ +

重要参数:

+ +

-d: seconds:指定 top 命令的刷新时间间隔,单位为秒;如,”top -d 5” 表示每 5 秒刷新一次 top 命令的输出;

+ +

-b:以批处理模式运行 top 命令,将结果输出到文件中,而不是交互式界面;

+ +

-p pid1,pid2,…:只显示指定进程 ID 的进程信息,多个进程 ID 之间用逗号分隔。

+ +

-n iterations:指定 top 命令的迭代次数,即 top 命令将输出多少次后自动退出。例如,”top -n 10” 表示 top 命令将输出 10 次后自动退出;

+ +

htop命令

+ +

显示系统中运行进程的状态和资源使用情况,界面更加美观。

+ +

atop:都是用于查看系统进程信息的命令,可以按照不同的方式显示进程信息。

+ +

vmstat:可以监控操作系统的进程状态、内存、虚拟内存、磁盘IO、CPU的信息。

+ +

-a:显示活动和非活动的内存使用情况;

+ +

-d:显示磁盘 I/O 统计信息

+ +
    +
  1. iostat:查看系统的磁盘 I/O 使用情况。
  2. +
  3. netstat:查看系统的网络连接情况。
  4. +
+ +

输出接果:

+ +

Proto:显示网络连接的协议类型,如 TCP、UDP 等。

+ +

Local Address 显示本地IP地址和端口号

+ +

Recv-Q:显示接收队列的大小

+ +

nmon:可以监测 CPU 使用率、内存使用情况、磁盘 I/O、网络流量、进程和线程等多个系统资源,还可以监测系统温度和风扇转速等硬件指标。

+ +

dmesg:来显示内核环缓冲区(kernel-ring buffer)内容。

+ +
    +
  1. +

    free:查看系统的内存使用情况,包括物理内存、交换内存(swap)和内核缓冲区内存。

    + +

    输出:

    + +

    buff/cache 列显示被 buffer 和 cache 使用的物理内存大小。

    +
  2. +
+ +

df disk free

+ +

显示磁盘分区上可以使用的磁盘空间;(通过文件系统来快速获取空间大小的信息,当我们删除一个文件的时候,这个文件不是马上就在文件系统当中消失了,而是暂时消失了,当所有程序都不用时,才会根据OS的规则释放掉已经删除的文件, df记录的是通过文件系统获取到的文件的大小,他比du强的地方就是能够看到已经删除的文件,而且计算大小的时候,把这一部分的空间也加上了,更精确了。当文件系统也确定删除了该文件后,这时候du与df就一致了。) +-a #查看全部文件系统,单位默认KB +-h #使用-h选项以KB、MB、GB的单位来显示,可读性高

+ +

du disk usage

+ +

显示每个文件和目录的磁盘使用空间,也就是文件的大小。(通过搜索文件来计算每个文件的大小然后累加,du能看到的文件只是一些当前存在的,没有被删除的。他计算的大小就是当前他认为存在的所有文件大小的累加和。)

+ +

-h : 以K M G为单位显示,提高可读性

+ +

–max-depth=1 : 显示层级

+ +

uptime:查看系统的运行时间和平均负载。

+ +

load average: 3.17, 3.45, 3.65,分别描述了1分钟5分钟15分钟内系统平均负载.

+ +

ps:查看系统的进程信息,包括进程 ID、CPU 占用率等。

+ +

-aux :显示所有进程,包括其他用户的进程。

+ +

aux格式:USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND

+ +

USER: 行程拥有者 +PID: pid +%CPU: 占用的 CPU 使用率 +%MEM: 占用的记忆体使用率 +VSZ: 占用的虚拟记忆体大小 +RSS: 占用的记忆体大小 +TTY: 终端的次要装置号码 (minor device number of tty) +STAT: 行程状态

+ +

查找指定进程(如Java)的进程号: +ps -ef | grep java

+ +

lsof:查看系统打开的文件和网络连接情况;lscpu:查看CPU信息.

+ +

tcpdump:抓取和分析网络数据包。

+ +

ping、traceroute、mtr:用于测试网络连接和延迟。

+ +

ping -c 5 www.baidu.com # -c 5表示发送5个数据包

+ +

traceroute命令用于追踪数据包在网络上的传输时的全部路径,它默认发送的数据包大小是40字节。通过traceroute我们可以知道信息从你的计算机到互联网另一端的主机是走的什么路径. +traceroute www.baidu.com

+ +

rsync:用于远程备份和同步文件和目录。

+ +

同步同一台机上的两个目录:

+ +
$ rsync -zvr /var/opt/installation/inventory/ /root/temp
+building file list ... done
+sva.xml
+svB.xml
+.
+sent 26385 bytes  received 1098 bytes  54966.00 bytes/sec
+total size is 44867  speedup is 1.63
+
+ +

-z –compress:压缩数据 +-v –verbose:显示详细信息 +-r –recursive:递归处理,将指定目录下的所有文件和子目录一并处理

+ +

还可以拷贝单个文件:

+ +
$ rsync -v /var/lib/rpm/Pubkeys /root/temp/
+Pubkeys
+
+sent 42 bytes  received 12380 bytes  3549.14 bytes/sec
+total size is 12288  speedup is 0.99
+
+ +

从本地拷贝多个文件到远端:

+ +
$ rsync -avz /root/temp/ thegeekstuff@192.168.200.10:/home/thegeekstuff/temp/
+Password:
+building file list ... done
+./
+rpm/
+rpm/Basenames
+rpm/Conflictname
+
+sent 15810261 bytes  received 412 bytes  2432411.23 bytes/sec
+total size is 45305958  speedup is 2.87
+
+ +

从本地拷贝多个文件到远端: +使用rsync, 也可以从本地拷贝多个文件或目录到远端.

+ +
$ rsync -avz /root/temp/ thegeekstuff@192.168.200.10:/home/thegeekstuff/temp/
+Password:
+building file list ... done
+./
+rpm/
+rpm/Basenames
+rpm/Conflictname
+
+sent 15810261 bytes  received 412 bytes  2432411.23 bytes/sec
+total size is 45305958  speedup is 2.87
+
+ +

从远程服务器拷贝文件到本地:

+ +
$ rsync -avz thegeekstuff@192.168.200.10:/var/lib/rpm /root/temp
+Password:
+receiving file list ... done
+rpm/
+rpm/Basenames
+.
+sent 406 bytes  received 15810230 bytes  2432405.54 bytes/sec
+total size is 45305958  speedup is 2.87
+
+ +

scp、sftp:用于远程复制和传输文件。

+ +

scp是一种基于SSH的协议,可在网络上的主机之间提供文件传输。

+ +

ssh、telnet:用于远程登录和管理系统。

+ +

telnet是明文传送, ssh是加密的且支持压缩。

+ +

crontab:用于定时执行任务。

+ +

systemctl、service:用于管理系统服务和启动、停止、重启服务。

+ +

firewall-cmd、iptables:用于管理系统防火墙和配置网络规则。

+ +

systemctl start firewalld # 启动防火墙 +systemctl stop firewalld # 关闭防火墙 +systemctl restart firewalld # 重启防火墙 +systemctl status firewalld # 查看防火墙状态 +systemctl enable firewalld # 设置开机启动

+ +

selinux:用于管理系统安全策略和访问控制。

+ +

sysctl:用于调整内核参数和系统性能优化。

+ +

sysctl命令被用于在内核运行时动态地修改内核的运行参数,可用的内核参数在目录/proc/sys中。它包含一些TCP/IP堆栈和虚拟内存系统的高级选项, 这可以让有经验的管理员提高引人注目的系统性能。用sysctl可以读取设置超过五百个系统变量。

+ +
vim /etc/sysctl.conf
+net.ipv4.ip_forward = 1 # 开启IP转发
+net.ipv4.conf.all.accept_redirects = 0 # 禁止所有接口接收重定向报文
+
+ +

ulimit:用于限制系统资源使用和保护系统安全。

+ +

perf、、systemtap:用于系统性能分析和瓶颈诊断。

+ +

perf、、systemtap:用于系统性能分析和瓶颈诊断。

+ +

init、systemd、upstart:用于系统进程管理和服务管理。

+ +

lspci、lsusb、lshw:用于硬件信息和诊断。

+ +

lp、lpr、cups:用于打印管理和打印队列控制。

+ +

tree、fd、rg:用于文件和目录搜索和查找。

+ +

2. 综合检测集成方案和工具

+ +

Unixbench

+ +

Unixbench是一个类unix系(如Unix, Linux)统下的性能测试工具,被广泛用在linux系统主机的性能的测试,主要提供的测试项目有:系统调用、读写、进程、图形化测试、2D、3D、管道、运算、C库等系统基准性能提供测试数据。

+ +

要运行UnixBench测试,请按照以下步骤进行:

+ +

打开终端并进入UnixBench的根目录。在大多数情况下,UnixBench的根目录位于/usr/share/UnixBench。您可以使用以下命令进入该目录:

+ +
    +
  1. cd /usr/share/UnixBench +如果UnixBench安装在其他位置,请相应地更改路径。
  2. +
  3. 运行./Run命令。这将会运行所有可用的测试,包括CPU、内存、磁盘、文件系统和网络等方面的测试。测试过程可能需要一些时间,具体时间取决于您的系统性能。
  4. +
  5. 等待测试完成后,将生成一个HTML格式的报告。该报告位于UnixBench根目录下的results文件夹中,文件名为UnixBench.html。您可以使用Web浏览器打开该文件,并查看测试的结果和性能数据。
  6. +
+ +

除了运行所有测试之外,您还可以运行单个测试或一组测试。以下是一些示例命令:

+ +

运行CPU测试:./Run cpu +运行内存测试:./Run memory +运行磁盘测试:./Run disk +运行文件系统测试:./Run fs +运行网络测试:./Run network

+ +

Sysbench工具

+ +

内建测试项

+ +

fileio:文件I/O测试。 +cpu:CPU性能测试。 +memory:内存速度测试。 +threads:线程子系统性能测试。 +mutex:互斥性能测试。 +oltp:OLTP(在线事务处理)测试。

+ +

其他工具

+ +

Nagios:一款开源的企业级 IT 基础设施监控软件,可监测服务器、网络设备、应用程序等多种设备和服务状态。

+ +

Zabbix:一款功能强大的开源网络监控软件,支持多种监测方式,如 SNMP、JMX、IPMI、HTTP 等,并具有自定义告警、数据可视化等功能。

+ +

Munin:一款基于 Web 的监测软件,可以监测 CPU、内存、磁盘、网络等多种系统指标,还支持插件扩展和数据可视化。

+ +

Cacti:一款基于 RRDtool 的网络图形化监测工具,可以监测网络带宽、路由器、交换机等设备的状态,并生成图表和报告。

+ +

Prometheus:一款开源的服务监测系统,可监测多种应用程序、数据库、中间件等服务状态,并提供数据可视化和告警功能。

+ +

Sysdig:一款基于系统调用跟踪的容器监测工具,可以监测容器的运行状态、资源使用情况、网络流量等,并提供数据可视化和安全分析功能。

+ +

Netdata:一款轻量级的实时系统监测工具,支持多种系统指标和网络流量监测,并具有数据可视化和告警功能。

+ +

网络监视器,如 tcpdump、Wireshark

+ +

3. 检测步骤

+ +

检查系统资源使用情况:可以使用 top 命令查看系统中正在运行的进程,以及它们占用的 CPU 和内存资源。可以使用 free 命令查看系统中可用的内存和交换空间的使用情况。

+ +

检查磁盘使用情况:可以使用 df 命令查看系统中磁盘分区的使用情况,以及剩余的可用空间。可以使用 du 命令查看特定目录或文件的磁盘使用情况。

+ +

检查网络带宽和连接情况:可以使用 iftop 命令查看系统中正在使用的网络连接和带宽使用情况。可以使用 netstat 命令查看特定端口或协议的网络连接情况。

+ +

检查系统负载和性能指标:可以使用 uptime 命令查看系统的负载情况。可以使用 vmstat 命令查看系统的虚拟内存使用情况和 CPU 使用情况。可以使用 iostat 命令查看系统的磁盘 I/O 使用情况。

+ +

进行基准测试:可以使用一些基准测试工具,如 UnixBench、Geekbench、Sysbench 等,对系统进行性能测试,以便比较不同系统或不同配置的性能差异。

+ +

4. 使用crontab定时调度任务进行自动化运维(对系统日志进行备份等)

+ +

有时候,为了系统稳定性,除了在/var/log通过回滚机制生成各种日志文件以排查系统故障之外,我们也想把这些关键的日志文件保存到指定文件夹,防止突然断电或者磁盘分区被破坏之后丢失关键的日志信息。

+ +

代码如下:(文件名为backup_sysconfig.sh)

+ +
#! /bin/bash
+# this script is used to backup the syslog file in /var/log dir everyday
+echo `date`
+cp /var/log/syslog /home/hugo/test_BASH/log/
+# adding the user with x permission
+chmod u+x /home/hugo/test_BASH/log/syslog
+# chnage the firname
+today=$(date +%y%m%d)
+echo $today
+mv /home/hugo/test_BASH/log/syslog /home/hugo/test_BASH/log/syslog_$today
+
+ +

定时调度任务

+ +

crontab 的语句是“分 时 日 月 周 命令”.

+ +

开启定时调度任务,每天凌晨1点执行一次

+ +
crontab -e
+# 0 1 * * * /home/hugo/test_BASH/backup_sysconfig.sh
+
+ +

显示 crontab: installing new crontab,表示定时任务已经开启。

+ +

查看定时任务

+ +
crontab -l
+
+ +

开启或者关闭定时任务

+ +
service crondstart
+service crondstop
+
+
+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/91/7a6f1ff22b1d0b8f41726999fa00eaee7bee43f9a1fce1de3505bebc16d412 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/91/7a6f1ff22b1d0b8f41726999fa00eaee7bee43f9a1fce1de3505bebc16d412 new file mode 100644 index 0000000000..ea0c13100b --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/91/7a6f1ff22b1d0b8f41726999fa00eaee7bee43f9a1fce1de3505bebc16d412 @@ -0,0 +1,8 @@ +I"  + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/91/e5d9c0ec9e91bd96613e7375121fd534504cae381cf711a7121b785259259e b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/91/e5d9c0ec9e91bd96613e7375121fd534504cae381cf711a7121b785259259e new file mode 100644 index 0000000000..2a3f6829ff --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/91/e5d9c0ec9e91bd96613e7375121fd534504cae381cf711a7121b785259259e @@ -0,0 +1,33 @@ +I"E + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/93/96ce43a514d99f10f89bf434507300f2bbec2342162cb5e65eb83ea0400579 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/93/96ce43a514d99f10f89bf434507300f2bbec2342162cb5e65eb83ea0400579 new file mode 100644 index 0000000000..1154de5d1e --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/93/96ce43a514d99f10f89bf434507300f2bbec2342162cb5e65eb83ea0400579 @@ -0,0 +1,2 @@ +I"2

1. 基本命令

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/97/f070ec086293858776e467ff152e9a83d8978da36f4bcf67b260d23788ab72 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/97/f070ec086293858776e467ff152e9a83d8978da36f4bcf67b260d23788ab72 new file mode 100644 index 0000000000..157eb32b31 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/97/f070ec086293858776e467ff152e9a83d8978da36f4bcf67b260d23788ab72 @@ -0,0 +1,3 @@ +I"

1. pytest简介

+

Pytest 是一个比较成熟且功能完备的 Python 测试框架。其提供完善的在线文档,并有着大量的第三方插件和内置帮助,适用于许多小型或大型项目。Pytest 灵活易学,打印调试和测试执行期间可以捕获标准输出,适合简单的单元测试到复杂的功能测试。还可以执行 nose, unittest 和 doctest 风格的测试用例,甚至 Django 和 trial。支持良好的集成实践, 支持扩展的 xUnit 风格 setup,支持非 python 测试。支持生成测试覆盖率报告,支持 PEP8 兼容的编码风格。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/9a/4861c553486b643a9709c0c334b6c3a2364e9c9f42a8736ff85842ab927123 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/9a/4861c553486b643a9709c0c334b6c3a2364e9c9f42a8736ff85842ab927123 new file mode 100644 index 0000000000..fdc89b7bec --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/9a/4861c553486b643a9709c0c334b6c3a2364e9c9f42a8736ff85842ab927123 @@ -0,0 +1,14 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/9c/87f7a9ec62311e22997bc3c28abc8eefbb00fa11e055f98b488453f31ca4d9 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/9c/87f7a9ec62311e22997bc3c28abc8eefbb00fa11e055f98b488453f31ca4d9 new file mode 100644 index 0000000000..cff1117616 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/9c/87f7a9ec62311e22997bc3c28abc8eefbb00fa11e055f98b488453f31ca4d9 @@ -0,0 +1,2 @@ +I"Q

软件测试分类思维导图

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/9e/c478316b106e4a004357a83f54f8b75f1676d56d499325893d59bdbccf7df7 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/9e/c478316b106e4a004357a83f54f8b75f1676d56d499325893d59bdbccf7df7 new file mode 100644 index 0000000000..e02b36bf77 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/9e/c478316b106e4a004357a83f54f8b75f1676d56d499325893d59bdbccf7df7 @@ -0,0 +1,2 @@ +I"E

定义自己的异常类

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/9f/0fae7a49763d3e9c784594d1900378cb5af58018ec70d1566c543f4a9754ab b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/9f/0fae7a49763d3e9c784594d1900378cb5af58018ec70d1566c543f4a9754ab new file mode 100644 index 0000000000..740a265d1c --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/9f/0fae7a49763d3e9c784594d1900378cb5af58018ec70d1566c543f4a9754ab @@ -0,0 +1,2 @@ +I"d

apt修改为阿里源, sudo vim /etc/apt/sources.list之后出现问题:

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/9f/202dfaed20036a9cab8c88cc3a3d51a4fe2c599476c1ae975472ea3d5f4aea b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/9f/202dfaed20036a9cab8c88cc3a3d51a4fe2c599476c1ae975472ea3d5f4aea new file mode 100644 index 0000000000..306e354931 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/9f/202dfaed20036a9cab8c88cc3a3d51a4fe2c599476c1ae975472ea3d5f4aea @@ -0,0 +1,6 @@ +I"a + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/a0/4ef86925c9b84e711522702ab48ab2d8482da070a4c23cc4ee5fabd90fe3a6 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/a0/4ef86925c9b84e711522702ab48ab2d8482da070a4c23cc4ee5fabd90fe3a6 new file mode 100644 index 0000000000..db489ec5b5 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/a0/4ef86925c9b84e711522702ab48ab2d8482da070a4c23cc4ee5fabd90fe3a6 @@ -0,0 +1,97 @@ +I"M

软件测试分类思维导图

+ +

软件测试分类思维导图

+ +

软件测试分类思维导图-文本模式

+
按阶段划分
+	单元测试
+		概念:指对软件中的最小可测单元的检测认证(一般是函数)
+		应用场景: 测试某个函数的功能是否正确
+	集成测试
+		概念: 在单元测试的基础上按照设计要求组装成子系统或系统进行测试
+		测试需求: 实践证明,一些模块可以单独工作,但是不能保证集成起来也能正常工作
+	系统测试
+		概念: 系统测试是将经过集成测试的软件和操作系统/硬件看成一个整体,在实际运行环境下测试
+		举例: 百度在浏览器和手机上不同操作系统,都要进行测试
+	验收测试
+		按对象划分
+			产品验收: 基于需求文档的需求验收
+			项目验收: 客户根据需求进行验证性测试
+		按阶段划分
+			α测试(内测版本)
+			β测试(公测版本)
+			γ测试(待发布版本)
+		负责人
+			产品: 产品经理
+			项目:客户(甲方)或第三方测评机构(乙方)
+
+按代码逻辑划分
+	黑盒测试
+		概念: 把测试对象看成一个不能打开的黑盒子,测试时候,完全不考虑盒子里面的代码逻辑结构和具体运行,只根据需求文档,检测程序功能是否符合文档中的说明,检测输出结果对不对
+		测试依据
+			需求文档
+		重点: 以客户的角度,从输入的数据和输出的数据的对应关系出发进行测试
+		分类
+			功能相关
+				功能测试
+					检查产品是否满足要求
+				界面测试
+					UI测试,如元素布局
+				易用性测试
+					用户体验
+				专项测试
+					如安装卸载升级、网络专项
+			性能相关
+				性能测试
+					模拟用户场景,测试系统的各项性能指标,查看是否满足要求
+				压力测试
+					在高负载、资源少的情况下运行测试,找出性能隐患
+				负载测试
+					不断增加负载,测试软件吞吐量上限,以验证系统的负载能力
+	白盒测试
+		概念:把测试对象看成一个打开的透明盒子,测试时候,测试人员利用内部逻辑结构以及相关信息,通过不同分支检查程序状态,检查程序中每条路线能否按照预定要求正确工作
+		测试依据
+			源代码
+		重点
+			必须检查程序的内部结构,从程序的逻辑着手,得出测试的数据(输入和输出的数据)
+		对应关系
+			单元测试:白盒
+			集成测试:白盒
+			系统测试:黑盒
+			验收测试:黑盒
+	灰盒测试
+		概念
+			介于黑盒与白盒测试之间,只关注一部分代码逻辑
+
+按是否运行代码划分
+	静态测试
+		概念: 静态测试是指不运行被测程序本身,通过分析或检查程序语法/结构/过程来检查程序的正确性
+		测试对象
+			文档
+				需求文档
+				各种设计文档
+			源代码
+				找出程序可能重复的地方
+				找出不安全
+	动态测试
+		概念: 动态测试就是通过运行被测程序,检查运行结果与预期结果的差距,并分析运行效率、健壮性
+		测试对象
+			系统
+		步骤
+			测试用例设计
+			执行测试用例
+			检查运行实际结果与预期结果
+
+按是否自动化执行划分
+	手工测试: 手工的方法去执行测试
+	自动化测试:需要借助工具和代码完成手工测试的工作
+
+其他
+	回归测试: 回归测试是指修改代码之后,重新进行测试,已确定修改没有引入新的错误
+	冒烟测试: 针对最基本的功能或者流程进行测试
+
+来源
+	https://blog.csdn.net/karsa__/article/details/108036552
+
+
+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/a0/5dd1b0ddce674bdfba0b58b5fb8d1b8f1a1d1e0c462a94f822f8e34f4e8f25 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/a0/5dd1b0ddce674bdfba0b58b5fb8d1b8f1a1d1e0c462a94f822f8e34f4e8f25 new file mode 100644 index 0000000000..8902ddef4f --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/a0/5dd1b0ddce674bdfba0b58b5fb8d1b8f1a1d1e0c462a94f822f8e34f4e8f25 @@ -0,0 +1,174 @@ +I"

回顾一下昨天有关引用传递与值传递有关的内容。

+ +

引用传递与值传递

+ +

可变对象为引用传递(传址),不可变对象为值传递(传值)。

+ +

传值和传址的区别 +传值就是传入一个参数的值,传址就是传入一个参数的地址,也就是内存的地址(相当于指针)。他们的区别是如果函数里面对传入的参数重新赋值,函数外的全局变量是否相应改变,用传值传入的参数是不会改变的,用传址传入就会改变

+ +

Python函数参数传递方式:传递对象引用(传值和传址的混合方式),如果是数字,字符串,元组(不可变对象)则传值;如果是列表,字典(可变对象)则传址;

+ +

举个例子:

+
a=1
+def f(a):
+    a+=1
+f(a)
+print(a)
+
+ +

结果如下:

+ +
1
+
+ +

这段代码里面,因为a是数字类型,所以是传值的方式,a的值并不会变,输出为1

+ +
a=[1]
+def f(a):
+    a[0]+=1
+f(a)
+print(a)
+
+ +

结果如下:

+ +
[2]
+
+ +

这段代码里面,因为a的类型是列表,所以是传址的形式,$a[0]$的值会改变,输出为$[2]$.

+ +

我们再打印一下a的地址:

+
def changelist(a):
+    a.append(0)
+    print(id(a))
+    print(a)
+a = [4,5,6]
+
+changelist(a)
+print("-"*60)
+print(id(a))
+print(a)
+
+

返回结果为:

+
2375999356032
+[4, 5, 6, 0]
+------------------------------------------------------------
+2375999356032
+[4, 5, 6, 0]
+
+

发现在函数内部和函数外部,a的地址是一样的,说明a是传址的,而且在函数内部改变了a的值,函数外部的a的值也改变了。

+ +

函数作用域

+ +

所谓作用域(Scope),就是变量的有效范围,就是变量可以在哪个范围以内使用。有些变量可以在整段代码的任意位置使用,有些变量只能在函数内部使用,有些变量只能在 for 循环内部使用。

+ +

Python局部变量

+ +

在函数内部定义的变量,它的作用域也仅限于函数内部,出了函数就不能使用了,我们将这样的变量称为局部变量(Local Variable)。

+ +

要知道,当函数被执行时,Python 会为其分配一块临时的存储空间,所有在函数内部定义的变量,都会存储在这块空间中。而在函数执行完毕后,这块临时存储空间随即会被释放并回收,该空间中存储的变量自然也就无法再被使用。

+ +

举例子:

+
def demo():
+    add = "http://c.biancheng.net/python/"
+    print("函数内部 add =",add)
+demo()
+print("函数外部 add =",add)
+
+ +

运行结果如下:

+
函数内部 add = http://c.biancheng.net/python/
+Traceback (most recent call last):
+
+  File "<ipython-input-3-3af67579cb4d>", line 5, in <module>
+    print("函数外部 add =",add)
+
+NameError: name 'add' is not defined
+
+ +

可以看到,如果试图在函数外部访问其内部定义的变量,Python 解释器会报 NameError 错误,并提示我们没有定义要访问的变量,这也证实了当函数执行完毕后,其内部定义的变量会被销毁并回收。

+ +

Python全局变量

+ +

除了在函数内部定义变量,Python 还允许在所有函数的外部定义变量,这样的变量称为全局变量(Global Variable)。

+ +

和局部变量不同,全局变量的默认作用域是整个程序,即全局变量既可以在各个函数的外部使用,也可以在各函数内部使用。

+ +

定义全局变量的方式有以下 2 种:

+
    +
  1. 在函数体外定义的变量,一定是全局变量,在函数内部也可以使用。 +
      +
    1. +
    +
  2. +
  3. 在函数体内定义全局变量。即使用 global 关键字对变量进行修饰后,该变量就会变为全局变量。
  4. +
+ +

查看变量作用域

+ +

globals()函数

+

globals() 函数为 Python 的内置函数,它可以返回一个包含全局范围内所有变量的字典,该字典中的每个键值对,键为变量名,值为该变量的值

+ +

举例子:

+
#全局变量
+Pyname = "Python教程"
+Pyadd = "http://c.biancheng.net/python/"
+def text():
+    #局部变量
+    Shename = "shell教程"
+    Sheadd= "http://c.biancheng.net/shell/"
+print(globals())
+
+

输出结果如下:

+
{ ...... , 'Pyname': 'Python教程', 'Pyadd': 'http://c.biancheng.net/python/', ......}
+
+

这个结果中,我们可以看到,全局变量 Pyname 和 Pyadd 的键值对都被包含在了 globals() 函数返回的字典中。

+ +

locals()函数

+ +

locals() 函数也是 Python 内置函数之一,通过调用该函数,我们可以得到一个包含当前作用域内所有变量的字典。这里所谓的“当前作用域”指的是,在函数内部调用 locals() 函数,会获得包含所有局部变量的字典;而在全局范文内调用 locals() 函数,其功能和 globals() 函数相同。

+ +
#全局变量
+Pyname = "Python教程"
+Pyadd = "http://c.biancheng.net/python/"
+def text():
+    #局部变量
+    Shename = "shell教程"
+    Sheadd= "http://c.biancheng.net/shell/"
+    print("函数内部的 locals:")
+    print(locals())
+text()
+print("函数外部的 locals:")
+print(locals())
+
+

程序执行结果为:

+
函数内部的 locals:
+{'Sheadd': 'http://c.biancheng.net/shell/', 'Shename': 'shell教程'}
+函数外部的 locals:
+{...... , 'Pyname': 'Python教程', 'Pyadd': 'http://c.biancheng.net/python/', ...... }
+
+

当使用 locals() 函数获取所有全局变量时,和 globals() 函数一样,其返回的字典中会默认包含有很多变量,这些都是 Python 主程序内置的,读者暂时不用理会它们。

+ +

var(object)函数

+ +

vars() 函数也是 Python 内置函数,其功能是返回一个指定 object 对象范围内所有变量组成的字典。如果不传入object 参数,vars() 和 locals() 的作用完全相同

+ +
Pyname = "Python教程"
+Pyadd = "http://c.biancheng.net/python/"
+class Demo:
+    name = "Python 教程"
+    add = "http://c.biancheng.net/python/"
+print("有 object:")
+print(vars(Demo))
+print("无 object:")
+print(vars())
+
+

程序执行结果为:

+
有 object:
+{...... , 'name': 'Python 教程', 'add': 'http://c.biancheng.net/python/', ......}
+无 object:
+{...... , 'Pyname': 'Python教程', 'Pyadd': 'http://c.biancheng.net/python/', ...... }
+
+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/a1/036f49cb34dbd5a746cd84ecac57c2c51e7507d3d2f636227dc78bd1390e28 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/a1/036f49cb34dbd5a746cd84ecac57c2c51e7507d3d2f636227dc78bd1390e28 new file mode 100644 index 0000000000..cb2611ed20 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/a1/036f49cb34dbd5a746cd84ecac57c2c51e7507d3d2f636227dc78bd1390e28 @@ -0,0 +1,419 @@ +I"B

00 写在前面

+

如何将ipynb文件转为markdown文件?

+
jupyter nbconvert --to markdown note.ipynb
+
+ +

chapter 01

+ +
import numpy as np
+# 向量 
+v = np.array([1,2,3])
+# 矩阵
+m = np.array([[1,2,3],[4,5,6],[7,8,9]])
+# 张量
+t = np.array([
+    [[1,2,3],[4,5,6],[7,8,9]],
+    [[11,12,13],[14,15,16],[17,18,19]],
+    [[21,22,23],[24,25,26],[27,28,29]],
+])
+print("向量:"+str(v))
+print("矩阵:"+str(m))
+print("张量:"+str(t))
+
+ +
向量:[1 2 3]
+矩阵:[[1 2 3]
+ [4 5 6]
+ [7 8 9]]
+张量:[[[ 1  2  3]
+  [ 4  5  6]
+  [ 7  8  9]]
+
+ [[11 12 13]
+  [14 15 16]
+  [17 18 19]]
+
+ [[21 22 23]
+  [24 25 26]
+  [27 28 29]]]
+
+ +

1.2 矩阵转置

+ +
a = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])
+a_t = a.transpose()
+print(a)
+# print one line to separate the two outputs
+print('-'*30)
+print(a_t)
+
+ +
[[ 1  2  3]
+ [ 4  5  6]
+ [ 7  8  9]
+ [10 11 12]]
+------------------------------
+[[ 1  4  7 10]
+ [ 2  5  8 11]
+ [ 3  6  9 12]]
+
+ +

1.3 矩阵加法

+

有时候允许矩阵和向量相加,得到一个矩阵,本质上是构造了一个将b按行复制的一个新矩阵,这种操作叫做广播(broadcasting)。

+ +

1.4 矩阵乘法

+

矩阵乘法的结果是一个矩阵,其第i行第j列的元素是矩阵A的第i行与矩阵B的第j列的内积。A的形状为m×n,B的形状为n×p,那么A×B的形状为m×p。矩阵乘法的计算可以用下面的公式表示: + \(c[i,j] = a[i,:] * b[:,j]\)

+ +
m1 = np.array([[1.0,3.0],[1.0,0.0]])
+m2 = np.array([[1.0,2.0],[3.0,5.0]])
+print(m1)
+print(m2)
+# 矩阵乘法
+m3 = np.dot(m1,m2)
+print(m3)
+# 矩阵按元素相乘
+m4 = np.multiply(m1,m2)
+# 矩阵按元素相乘
+m5 = m1*m2
+print(m4)
+print(m5)
+
+ +
[[1. 3.]
+ [1. 0.]]
+[[1. 2.]
+ [3. 5.]]
+[[10. 17.]
+ [ 1.  2.]]
+[[1. 6.]
+ [3. 0.]]
+[[1. 6.]
+ [3. 0.]]
+
+ +

1.5 单位矩阵

+

单位矩阵是一个方阵,对角线上的元素为1,其余元素为0。单位矩阵的作用是不改变矩阵的值,它乘以任何矩阵都等于原矩阵。

+ +
np.identity(3)
+
+
array([[1., 0., 0.],
+       [0., 1., 0.],
+       [0., 0., 1.]])
+
+ +

1.6 矩阵的逆

+

矩阵A的逆(inverse)是一个矩阵B,使得A×B=I,其中I是单位矩阵。矩阵A的逆记为$A^{−1}$。如果A是一个方阵,那么A的逆也是一个方阵。如果A的逆存在,那么A的逆也叫做A的伪逆(pseudo-inverse)。

+ +
A = [[1,2],[4,5]]
+A_inv = np.linalg.inv(A) 
+print(A_inv)
+
+
[[-1.66666667  0.66666667]
+ [ 1.33333333 -0.33333333]]
+
+ +

1.7范数 norm

+

向量$L^P$范数定义为:
+\(||x||_p = (\sum_{i=1}^n |x_i|^p)^{1/p},p\geq 1,p\in\mathbb{R}\)

+ +

L1范数:向量中各个元素绝对值之和
+\(||x||_1 = \sum_{i=1}^n |x_i|\)

+ +

L0范数:向量中非零元素的个数
+\(||x||_0 = \sum_{i=1}^n \mathbb{1}(x_i\neq 0)\)

+ +

L2范数:向量中各个元素平方和的平方根,也叫欧式范数,是向量x到原点的欧几里得距离,有时候也用L2范数的来衡量向量:$x^Tx$.
+\(||x||_2 = \sqrt{\sum_{i=1}^n x_i^2}\)

+ +

L∞范数:向量中各个元素绝对值的最大值
+\(||x||_\infty = \max_{i=1,\cdots,n}|x_i|\)

+ +

机器学习中常用的范数有L1范数和L2范数,L1范数用于稀疏性的优化,L2范数用于凸优化。

+ +
import numpy as np
+a = np.array([1.0,3.0,5.0])
+print("向量的2范数:"+str(np.linalg.norm(a,ord=2)))
+print("向量的1范数:"+str(np.linalg.norm(a,ord=1)))
+print("向量的无穷范数:"+str(np.linalg.norm(a,ord=np.inf)))
+# 矩阵才有Frobenius范数
+b = np.array([[1.0,3.0,5.0],[2.0,4.0,6.0]])
+print('向量的Frobenius范数:'+str(np.linalg.norm(b,ord='fro')))
+
+ +
向量的2范数:5.916079783099616
+向量的1范数:9.0
+向量的无穷范数:5.0
+向量的Frobenius范数:9.539392014169456
+
+ +

1.8 特征值分解

+

如果一个n×n的矩阵A有n组线性无关的单位特征向量{$v^1$, $v^2$, $\cdots$, $v^n$},并且对应的特征值为{$\lambda_1$, $\lambda_2$, $\cdots$, $\lambda_n$},那么矩阵A可以被分解为:
+ \(A = V diag(\lambda) V^{-1}\)

+ +

不是所有的矩阵都有特征值分解,如果A是一个n×n的矩阵,那么A的特征值分解存在当且仅当A是可逆的且A的行列式不为0。如果A的特征值分解存在,那么A的特征值分解是唯一的。

+ +
A = np.array([[1,2,3],[4,5,6],[7,8,9]])
+# 计算特征值
+eig_value,eig_vector = np.linalg.eig(A)
+print("特征值:"+str(eig_value))
+print("特征向量:"+str(eig_vector))
+
+ +
特征值:[ 1.61168440e+01 -1.11684397e+00 -1.30367773e-15]
+特征向量:[[-0.23197069 -0.78583024  0.40824829]
+ [-0.52532209 -0.08675134 -0.81649658]
+ [-0.8186735   0.61232756  0.40824829]]
+
+ +

1.9 奇异值分解

+

奇异值分解(Singular Value Decomposition,SVD)是一种矩阵分解方法,它将一个矩阵分解为奇异向量和奇异值,它的分解形式如下:
+ \(A = U\Sigma V^T\)

+ +

若A是m×n的矩阵,那么U是m×m的正交矩阵(其列向量称为左奇异向量),V是n×n的正交矩阵(其列向量称为右奇异向量),$\Sigma$是m×n的对角矩阵(对角线上的元素称为奇异值)。奇异值分解的作用是将一个矩阵分解为三个矩阵的乘积,这三个矩阵分别是正交矩阵和对角矩阵,这样就可以将矩阵的秩降低,从而达到降维的目的。

+ +

事实上,左奇异向量式$AA^T$的特征向量,右奇异向量是$A^TA$的特征向量,奇异值是$AA^T$特征值的平方根。

+ +
A = np.array([[1,2,3],[4,5,6],[7,8,9]])
+U,D,V= np.linalg.svd(A)
+print("U:"+str(U))
+print("D:"+str(D))
+print("V:"+str(V))
+
+ +
U:[[-0.21483724  0.88723069  0.40824829]
+ [-0.52058739  0.24964395 -0.81649658]
+ [-0.82633754 -0.38794278  0.40824829]]
+D:[1.68481034e+01 1.06836951e+00 4.41842475e-16]
+V:[[-0.47967118 -0.57236779 -0.66506441]
+ [-0.77669099 -0.07568647  0.62531805]
+ [-0.40824829  0.81649658 -0.40824829]]
+
+ +

1.10 PCA分解

+

假设我们有m个数据点$x^1,x^2,\cdot,x^m \in \mathbb{R}^n$,每个数据点是一个n维的向量,我们希望将这些数据点投影到一个l维的空间中(降纬之后的损失信息尽可能地少),使得投影后的数据点之间的距离尽可能的大,而投影后的数据点与原始数据点之间的距离尽可能的小。这就是PCA分解。 +PCA分解是线性变化,假设$x^i$分解之后的对应点为$c^i$,那么有:
+ \(f(x) =c\)
+ \(c \approx =g(f(x))\)
+ \(g(c)=Dc,D \in \mathbb{R}^{n\times l}\)
+为了计算方便,我们将这个矩阵的列向量约束为相互正交的;而且,考虑到尺度缩放的问题,我们将这个矩阵的列向量约束为具有单位范数来获得唯⼀解。 +对于给定的x,我们需要找到信息损失最小的$\boldsymbol{c}^{\star}$:
+\(\boldsymbol{c}^{\star}=\arg \min _{\boldsymbol{c}}\|\boldsymbol{x}-g(\boldsymbol{c})\|_2=\arg \min _{\boldsymbol{c}}\|\boldsymbol{x}-g(\boldsymbol{c})\|_2^2\)

+ +

这里我们用二范数来衡量信息的损失。展开之后我们有:
+\(\|\boldsymbol{x}-g(\boldsymbol{c})\|_2^2=(\boldsymbol{x}-g(\boldsymbol{c}))^{\top}(\boldsymbol{x}-g(\boldsymbol{c}))=\boldsymbol{x}^{\top} \boldsymbol{x}-2 \boldsymbol{x}^{\top} g(\boldsymbol{c})+g(\boldsymbol{c})^{\top} g(\boldsymbol{c})\)

+ +

结合 $g(\boldsymbol{c})$ 的表达式, 忽略不依赖 $\boldsymbol{c}$ 的 $\boldsymbol{x}^{\top} \boldsymbol{x}$ 项, 我们有:
+\(\begin{aligned} +\boldsymbol{c}^{\star} & =\arg \min _{\boldsymbol{c}}-2 \boldsymbol{x}^{\top} \boldsymbol{D} \boldsymbol{c}+\boldsymbol{c}^{\top} \boldsymbol{D}^{\top} \boldsymbol{D} \boldsymbol{c} \\ +& =\arg \min _{\boldsymbol{c}}-2 \boldsymbol{x}^{\top} \boldsymbol{D} \boldsymbol{c}+\boldsymbol{c}^{\top} \boldsymbol{I}_l \boldsymbol{c} \\ +& =\arg \min _{\boldsymbol{c}}-2 \boldsymbol{x}^{\top} \boldsymbol{D} \boldsymbol{c}+\boldsymbol{c}^{\top} \boldsymbol{c} +\end{aligned}\)

+ +

这里 $\boldsymbol{D}$ 具有单位正交性。 +对 $\boldsymbol{c}$ 求梯度, 并令其为零, 我们有:

+ +\[\begin{aligned} +\nabla_{\boldsymbol{c}}\left(-2 \boldsymbol{x}^{\top} \boldsymbol{D} \boldsymbol{c}+\boldsymbol{c}^{\top} \boldsymbol{c}\right) & =\mathbf{0} \\ +-2 \boldsymbol{D}^{\top} \boldsymbol{x}+2 \boldsymbol{c} & =\mathbf{0} \\ +\boldsymbol{c} & =\boldsymbol{D}^{\top} \boldsymbol{x} +\end{aligned}\] + +

因此, 我们的编码函数为:
+\(f(x)=D^{\top} \boldsymbol{x}\)

+ +

此时通过编码解码得到的 $重构\boldsymbol{x}$ 为:
+\(r(x)=g(f(x))=DD^{\top}x\)

+ +

接下来求解最优的变换 $\boldsymbol{D}$ 。由于我们需要将 $\boldsymbol{D}$ 应用到所有的 $\boldsymbol{x}_i$ 上,所以我们需要最优化: +\(\begin{array}{l} +\boldsymbol{D}^{\star}=\arg \min _{\boldsymbol{D}} \sqrt{\sum_{i, j}\left(\boldsymbol{x}_j^{(i)}-r\left(\boldsymbol{x}^{(i)}\right)_j\right)^2} \\ +\text { s.t. } \boldsymbol{D}^{\top} \boldsymbol{D}=\boldsymbol{I}_l +\end{array}\) +
+为了方便, 我们考虑 $l=1$ 的情况, 此时问题简化为: +\(\begin{array}{l} +\left.\boldsymbol{d}^{\star}=\arg \min _{\boldsymbol{d}} \sum_i\left(\boldsymbol{x}_j^{(i)}-\boldsymbol{d} \boldsymbol{d}^{\top} \boldsymbol{x}^{(i)}\right)\right)^2 \\ +\text { s.t. } \boldsymbol{d}^{\top} \boldsymbol{d}=1 +\end{array}\) +
+考虑 $\mathrm{F}$ 范数, 并进一步的推导:
+\(\begin{array}{l} +\boldsymbol{d}^{\star}=\arg \max _{\boldsymbol{d}} \operatorname{Tr}\left(\boldsymbol{d}^{\top} \boldsymbol{X}^{\top} \boldsymbol{X} \boldsymbol{d}\right) \\ +\text { s.t. } \boldsymbol{d}^{\top} \boldsymbol{d}=1 +\end{array}\)

+ +

优化问题可以用特征值分解来求解。

+ +

实际计算中,PCA分解的实现是基于SVD分解的,即假设有一个 $m \times n$ 的矩阵 $\boldsymbol{X}$, 数据的均值为零, 即 $\mathbb{E}[\boldsymbol{x}]=0, \boldsymbol{X}$ 对应的无偏样本协方差矩阵: $\operatorname{Var}[\boldsymbol{x}]=\frac{1}{m-1} \boldsymbol{X}^{\top} \boldsymbol{X}^{\mathrm{d}}$

+ +

PCA 是通过线性变换找到一个 $\operatorname{Var}[c]$ 是对角矩阵的表示 $\boldsymbol{c}=\boldsymbol{V}^{\top} \boldsymbol{x}$, 矩阵 $\boldsymbol{X}$ 的主成分可以通过奇异值分解 (SVD) 得到, 也就是说主成分是 $\boldsymbol{X}$ 的右奇异向量。假设 $\boldsymbol{V}$ 是 $\boldsymbol{X}=\boldsymbol{U} \boldsymbol{\Sigma} \boldsymbol{V}^{\top}$ 奇异值分解的右奇异向量, 我们得到原来的特征向量方程: +\(\boldsymbol{X}^{\top} \boldsymbol{X}=\left(\boldsymbol{U} \boldsymbol{\Sigma} \boldsymbol{V}^{\top}\right)^{\top} \boldsymbol{U} \boldsymbol{\Sigma} \boldsymbol{V}^{\top}=\boldsymbol{V} \boldsymbol{\Sigma}^{\top} \boldsymbol{U}^{\top} \boldsymbol{U} \boldsymbol{\Sigma} \boldsymbol{V}^{\top}=\boldsymbol{V} \boldsymbol{\Sigma}^2 \boldsymbol{V}^{\top}\) +因为根据奇异值的定义 $\boldsymbol{U}^{\top} \boldsymbol{U}=\boldsymbol{I}$ 。因此 $\boldsymbol{X}$ 的方差可以表示为: $\operatorname{Var}[\boldsymbol{x}]=\frac{1}{m-1} \boldsymbol{X}^{\top} \boldsymbol{X}=\frac{1}{m-1} \boldsymbol{V} \boldsymbol{\Sigma}^2 \boldsymbol{V}^{\top}$ 。 +所以 $\boldsymbol{c}$ 的协方差满足: $\operatorname{Var}[\boldsymbol{c}]=\frac{1}{m-1} \boldsymbol{C}^{\top} \boldsymbol{C}=\frac{1}{m-1} \boldsymbol{V}^{\top} \boldsymbol{X}^{\top} \boldsymbol{X} \boldsymbol{V}=\frac{1}{m-1} \boldsymbol{V}^{\top} \boldsymbol{V} \boldsymbol{\Sigma}^2 \boldsymbol{V}^{\top} \boldsymbol{V}=\frac{1}{m-1} \boldsymbol{\Sigma}^2$, 因为根据奇异值定义 $\boldsymbol{V}^{\top} \boldsymbol{V}=\boldsymbol{I}$ 。 $\boldsymbol{c}$ 的协方差是对 角的, $c$ 中的元素是彼此无关的。

+ +
# 以 iris 数据为例,展⽰ PCA 的使⽤。
+import pandas as pd
+import numpy as np
+from sklearn.datasets import load_iris
+import matplotlib.pyplot as plt
+from sklearn.preprocessing import StandardScaler
+%matplotlib inline
+
+ # 载入数据
+iris = load_iris()
+df = pd.DataFrame(iris.data, columns=iris.feature_names)
+df['label'] = iris.target
+df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
+# 查看数据
+df.tail()
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sepal lengthsepal widthpetal lengthpetal widthlabel
1456.73.05.22.32
1466.32.55.01.92
1476.53.05.22.02
1486.23.45.42.32
1495.93.05.11.82
+
+ +
# 准备数据
+X = df.iloc[:,0:4]# 取前4列为特征
+y =df.iloc[:,4]# 最后一列为标签
+
+ +
class PCA():
+    def __init(self):
+        pass
+    def fit(self,X,n_components):
+        n_samples = np.shape(X)[0]
+        covariance_matrix = 1/(n_samples-1) * (X-X.mean(axis=0)).T.dot(X-X.mean(axis=0))
+        # 对协方差矩阵进行特征值分解
+        eig_vals, eig_vecs = np.linalg.eig(covariance_matrix)
+        # 对特征值进行降序排序
+        idx = eig_vals.argsort()[::-1]
+        eig_vals = eig_vals[idx][:n_components] # 取前n个特征值
+        # atleast_1d函数将输入转换为至少为1维的数组
+        eig_vecs = np.atleast_1d(eig_vecs[:,idx])[:,:n_components] 
+
+        # 得到降纬之后的数据
+        X_transformed = X.dot(eig_vecs)
+        return X_transformed
+
+
+ +
model = PCA()
+Y = model.fit(X,2)# 降维到2维
+
+ +
# 降维后的数据,2列,分别是pca1和pca2,columns表示列名
+pcaDf = pd.DataFrame(np.array(Y),columns=['pca1','pca2']) 
+Df = pd.concat([pcaDf,y],axis=1)# axis =1表示列合并
+ax= plt.figure(figsize = (5,5))
+ax = plt.subplot(1,1,1)
+ax.set_xlabel('pca1',fontsize = 15)
+ax.set_ylabel('pca2',fontsize = 15)
+
+targets = [0,1,2] # 3个类别
+colors = ['r','g','b'] # 3种颜色,表示3个类别
+# for target,color in zip(targets,colors):
+#     ax.scatter(Df[Df['label']==target]['pca1'],Df[Df['label']==target]['pca2'],c=color,s=50)
+for target,color in zip(targets,colors):
+    indicesToKeep = Df['label'] == target
+    ax.scatter(Df.loc[indicesToKeep,'pca1'],Df.loc[indicesToKeep,'pca2'],c=color,s =50)
+ax.legend(targets)
+# title
+ax.set_title('PCA of IRIS dataset')
+ax.grid()
+
+ +

png

+ +
# 使用sklearn实习PCA
+from sklearn.decomposition import PCA
+sklearn_pca = PCA(n_components=2)
+Y = sklearn_pca.fit_transform(X)
+
+ +
PCADf = pd.DataFrame(np.array(Y),columns=['pca1','pca2'])
+Df = pd.concat([PCADf,y],axis=1)
+ax= plt.figure(figsize = (5,5))
+ax = plt.subplot(1,1,1)
+ax.set_xlabel('pca1',fontsize = 15)
+ax.set_ylabel('pca2',fontsize = 15)
+targets = [0,1,2] # 3个类别
+colors = ['r','g','b'] # 3种颜色,表示3个类别
+for target,color in zip(targets,colors):
+    indicesToKeep = Df['label'] == target
+    ax.scatter(Df.loc[indicesToKeep,'pca1'],Df.loc[indicesToKeep,'pca2'],c=color,s =50)
+ax.legend(targets)
+# title
+ax.set_title('PCA of IRIS dataset')
+ax.grid()
+
+ +

png

+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ab/7635c37c2ea63419e2d29da2e26636a5b6ea159470e6ddd7819f60923b197e b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ab/7635c37c2ea63419e2d29da2e26636a5b6ea159470e6ddd7819f60923b197e new file mode 100644 index 0000000000..f4ee2778bc --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ab/7635c37c2ea63419e2d29da2e26636a5b6ea159470e6ddd7819f60923b197e @@ -0,0 +1,253 @@ +I"$

主要包括日志文件介绍以及快速使用,文件封装,日志配置文件重构,日志工具类应用等等内容。

+ +

日志文件

+

简介

+

logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级/日志保存路径等

+

基本使用

+

在testcase文件夹下面,新建t_log文件夹,创建log_demo.py文件,这个文件夹用来存储日志有关的测试用例。

+
# 1. 导入logging
+import logging
+# 2. set the basic information
+logging.basicConfig(level = logging.INFO,format='%(asctime)s-%(levelname)s-%(message)s') 
+# format: 时间-日志等级-日志信息 %括号里面的内容可以自定义
+# level: INFO表示,只有INFO级别的日志才会被输出,如INFO,WARNING,ERROR,CRITICAL
+# 如果是DEBUG级别的日志,就不会被输出
+#3、定义日志名称getlogger
+logger = logging.getLogger("log_demo")
+#4、info,debug
+logger.info("info") # 只有INFO级别的日志才会被输出
+logger.debug("debug") # 不会被输出
+logger.warning("warning") # WARNING级别的日志会被输出
+
+ +
执行这段代码,结果如下:
+```console
+2023-01-17 09:28:54,466-INFO-info
+2023-01-17 09:28:54,466-WARNING-warning
+
+ +

日志输出到控制台,文件,指定文件

+
    +
  1. 设置logger名称
  2. +
  3. 设置logger级别
  4. +
  5. 创建handler,用于输出控制台或者写入日志文件
  6. +
  7. 设置日志级别
  8. +
  9. 定义handler的输出格式
  10. +
  11. 给logger添加handler
  12. +
+ +

在testcase文件夹下面的t_log文件夹下面,创建log_file_demo.py文件,输出到控制台.

+ +
# 输出到控制台
+import logging
+# 1. 设置logger名称
+logger = logging.getLogger("log_file_demo")
+# 2. 设置logger级别,这里设置为INFO,只有INFO级别的日志才会被输出
+logger.setLevel(logging.INFO)
+# 3. 创建handler,用于输出控制台或者写入日志文件
+fh_stream = logging.StreamHandler() # StreamHandler表示输出到控制台
+# 4. 设置日志级别
+fh_stream.setLevel(logging.INFO) 
+# 5. 定义handler的输出格式,这里设置为时间-日志等级-日志信息
+formatter =  logging.Formatter('%(asctime)s %(name)s %(levelname)s')
+fh_stream.setFormatter(formatter)
+# 6. 给logger添加handler,这里添加的是fh_stream,也就是输出到控制台,如果想输出到文件,可以添加fh_file
+logger.addHandler(fh_stream)
+#7、运行输出
+logger.info("this is a info")
+logger.debug("this is a debug")
+logger.warning("this is a warning")
+
+ +

在控制台查看输出结果:

+
2023-01-17 10:04:31,711 log_file_demo INFO
+2023-01-17 10:04:31,711 log_file_demo WARNING
+
+ +

输出到文件:

+
#输出到文件
+import logging
+#1、设置logger名称
+logger = logging.getLogger("log_file_demo")
+#2、设置log级别
+logger.setLevel(logging.INFO)
+#3、创建handler
+#写入文件
+fh_file = logging.FileHandler("./test.log")
+#4、设置日志级别
+fh_file.setLevel(logging.WARNING)
+#5、定义输出格式
+formatter = logging.Formatter('%(asctime)s %(name)s %(levelname)s %(message)s ')
+fh_file.setFormatter(formatter)
+#6、添加handler
+logger.addHandler(fh_file)
+#7、运行输出
+logger.info("this is a info")
+logger.debug("this is a debug")
+logger.warning("this is a warning")
+
+ +

结果会输出到同文件目录下的test.log文件中,内容如下:

+
2023-01-17 10:07:10,826 log_file_demo WARNING this is a warning 
+
+

为啥这里只有warning级别的日志被输出到文件中呢? +虽然日志级别设置为INFO,但是在添加handler的时候,设置了日志级别为WARNING,所以只有WARNING级别的日志会被输出到文件中。

+ +

Format格式说明

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
格式说明
%(name)sLogger的名字
%(levelno)s数字形式的日志级别
%(levelname)s文本形式的日志级别
%(pathname)s调用日志输出函数的模块的完整路径名,可能没有
%(filename)s调用日志输出函数的模块的文件名
%(asctime)s字符串形式的当前时间。默认格式是“2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(message)s用户输出的消息
+ +

日志文件的封装

+ +
    +
  1. 封装Log工具类 +在utils文件夹下面创建LogUtil.py文件,封装Log工具类,代码如下:
  2. +
+ +
# 创建类
+import logging
+# 定义日志级别关系映射
+# 定义之后,就可以用log_l[self.log_level]去更新原来的self.log_level
+log_l = {
+    "debug":logging.DEBUG,
+     "info":logging.INFO,
+     "warning":logging.WARNING,
+     "error":logging.ERROR
+}
+class Logger:
+# 定义参数
+## 输出文件名称,Loggername,日志级别
+    def __init__(self,log_file,log_name,log_level):
+        self.log_file = log_file
+        self.log_name = log_name
+        self.log_level = log_level
+        # 编写输出控制台或者文件,利用之前写好的log_file_demo.py
+       #1、设置logger名称
+        self.logger = logging.getLogger(self.log_name)
+        #2、设置log级别
+        self.logger.setLevel(log_l[self.log_level])
+        # 首先判断handler是否存在     创建handler
+        if not self.logger.handlers:
+
+         formatter = logging.Formatter('%(asctime)s %(name)s %(levelname)s %(message)s ')
+         # 输出到控制台
+         fh_stream = logging.StreamHandler()
+         fh_stream.setLevel(log_l[self.log_level])
+         fh_stream.setFormatter(formatter)
+     
+         #写入文件
+         fh_file = logging.FileHandler(self.log_file)
+         fh_file.setLevel(log_l[self.log_level])
+         fh_file.setFormatter(formatter)
+         #6、添加handler
+         self.logger.addHandler(fh_file)
+         self.logger.addHandler(fh_stream)
+
+
+
    +
  1. 重构配置文件
  2. +
+ +

根据我们封装的LogUtil.py文件,重构配置文件,需要的配置信息包括: log_file,log_lovel

+ +

log_name作为参数传入,不写在配置文件中。

+ +

打开config文件夹下面的conf.yml文件,修改配置信息,添加如下信息代码如下:

+
BASE:
+  # log
+  log_level: "debug"
+  log_extension: ".log"
+
+ +

然后修改config.py文件,添加如下代码(第一步,定义logs文件路径;第二步,获取该Log文件路径;第三步,根据该路经获取日志级别和文件扩展名:

+
# 定义logs文件路径
+_log_path = base_dir + os.sep + "logs" #下划线开头的变量,表示私有变量
+# 获取该Log文件路径
+def get_log_path():
+    """
+    获取log文件路径
+    """
+    return _log_path
+# 根据该路经获取日志级别和文件扩展名
+def get_conf_log(self):
+    return self.config["BASE"]["log_level"]
+# 获取文件扩展名
+def get_config_log_extension(self):
+    return self.config["BASE"]["log_extension"]
+
+ +

测试以下,在config.py文件的if name == ‘main‘:中执行代码:

+
if __name__ == '__main__':
+    conf_read = ConfigYaml()
+    # print(conf_read.get_conf_url())
+    print(conf_read.get_conf_log())
+    print(conf_read.get_config_log_extension())
+
+
    +
  1. 日志工具应用
  2. +
+ +

在LogUtil.py文件中,首先初始化参数数据,然后对外提供方法,初始化log工具类,提供其他类使用。

+ +
from config import config
+from config.config import ConfigYaml
+import datetime
+import os
+
+# 初始化参数数据
+# 日志文件路径
+log_path = config.get_log_path()
+# 获取当前时间
+current_time = datetime.datetime.now().strftime("%Y-%m-%d")
+# 获取扩展名
+log_extension = ConfigYaml.get_config_log_extension()
+logfile = os.path.join(log_path+current_time+log_extension)
+# 日志文件级别
+loglevel = ConfigYaml.get_conf_log()
+
+# 2. 对外方法,初始化log工具类,提供其他类使用
+def my_log():
+    return Logger(log_file=logfile,log_name=log_name,log_level=loglevel).logger
+
+if __name__ == "__main__":
+    my_log().debug("this is a bug")
+
+
+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ab/77c6de971ef7774e392b885bdd56b99c37213311bef3f1fd11177fd4914b8f b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ab/77c6de971ef7774e392b885bdd56b99c37213311bef3f1fd11177fd4914b8f new file mode 100644 index 0000000000..e2db82c039 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ab/77c6de971ef7774e392b885bdd56b99c37213311bef3f1fd11177fd4914b8f @@ -0,0 +1,2 @@ +I"/

安装Cockpit

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ac/600f030df7b5791a1baf804bb10655a9211ce40e8f58907e14b82e962df415 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ac/600f030df7b5791a1baf804bb10655a9211ce40e8f58907e14b82e962df415 new file mode 100644 index 0000000000..53c0110088 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ac/600f030df7b5791a1baf804bb10655a9211ce40e8f58907e14b82e962df415 @@ -0,0 +1,21 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ae/1bb32df60fb9c0c8f0fb7bd886f9236d694290e157c2d3eea9fdedc4a7a308 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ae/1bb32df60fb9c0c8f0fb7bd886f9236d694290e157c2d3eea9fdedc4a7a308 new file mode 100644 index 0000000000..a8f6472fb7 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ae/1bb32df60fb9c0c8f0fb7bd886f9236d694290e157c2d3eea9fdedc4a7a308 @@ -0,0 +1,136 @@ +I"

准备工作

+ +

shp文件是一个矢量文件,可以用来绘制地图,包括guangdong.dbf(属性表)、guangdong.shp(地图信息)、guangdong.shx(索引信息)。

+ +

cursor是什么?

+ +

Cursor.so 是一个集成了 GPT-4 的国内直接可以访问的,优秀而强大的免费代码生成器,可以帮助你快速编写、编辑和讨论代码。 + 它支持多种编程语言,如 Python, Java, C#, JavaScript 等,并且可以根据你的输入和需求自动生成代码片段。 + Cursor.so 还可以帮助你重构、理解和优化代码,提高开发效率。

+ +

官网地址:https://www.cursor.so/

+ +

代码生成

+ +

使用geopandas

+ +
import geopandas as gpd
+import matplotlib.pyplot as plt
+
+# Read the shapefile
+gdf = gpd.read_file('guangdong.shp', encoding='utf-8')
+
+# Plot the map
+gdf.plot()
+
+# Show the plot
+plt.show()
+
+ +

结果如下:

+ +

20230326135111

+ +

folium和geopandas

+ +

folium 建立在 Python 生态系统的数据应用能力和 Leaflet.js 库的映射能力之上,在Python中操作数据,然后通过 folium 在 Leaflet 地图中可视化。

+ +
import geopandas as gpd
+import folium
+
+# Read the shapefile
+gdf = gpd.read_file('guangdong.shp', encoding='utf-8')
+
+# Set the CRS of the GeoDataFrame
+gdf = gdf.set_crs('EPSG:4326')
+
+# Create a new map
+m = folium.Map(location=[23.5, 116.5], zoom_start=7)
+
+# Add the shapefile to the map
+folium.GeoJson(gdf).add_to(m)
+
+# Save the map as an HTML file
+m.save('map.html')
+
+ +

结果如下:

+ +

20230326135152

+ +

使用shapefile

+ +
import shapefile
+import matplotlib.pyplot as plt
+
+# Read the shapefile
+sf = shapefile.Reader("guangdong.shp")
+
+# Plot the map
+for shape in sf.shapeRecords():
+    x = [i[0] for i in shape.shape.points[:]]
+    y = [i[1] for i in shape.shape.points[:]]
+    plt.plot(x, y)
+
+# Show the plot
+plt.show()
+
+# %%
+import shapefile
+import matplotlib.pyplot as plt
+
+# Read the shapefile
+sf = shapefile.Reader("guangdong.shp")
+
+ +

shapefile2

+ +
import shapefile
+import matplotlib.pyplot as plt
+
+# Read the shapefile
+sf = shapefile.Reader("guangdong.shp")
+
+# Plot the map
+for shape in sf.shapeRecords():
+    x, y = zip(*shape.shape.points)
+    plt.plot(x, y)
+
+# Show the plot
+plt.show()
+
+ +

结果如下:

+ +

20230326135239

+ +

使用basemap

+ +
from mpl_toolkits.basemap import Basemap
+import shapefile
+import matplotlib.pyplot as plt
+
+# Read the shapefile
+sf = shapefile.Reader("guangdong.shp")
+
+# Create a new map
+m = Basemap(llcrnrlon=109, llcrnrlat=20, urcrnrlon=118, urcrnrlat=26, projection='lcc', lat_1=33, lat_2=45, lon_0=100)
+
+# Plot the map
+for shape in sf.shapeRecords():
+    x, y = zip(*shape.shape.points)
+    x, y = m(x, y)
+    m.plot(x, y, 'k-', linewidth=0.5)
+
+# Show the plot
+plt.show()
+
+ +

结果如下:

+ +

20230326135308

+ +

项目地址

+ +

guangdong_map

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b0/8cb70a1ff17a91ce4cc14f8a9e3ea3e874a647835dc9e4a41ab58aaf848980 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b0/8cb70a1ff17a91ce4cc14f8a9e3ea3e874a647835dc9e4a41ab58aaf848980 new file mode 100644 index 0000000000..f639b51e3e --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b0/8cb70a1ff17a91ce4cc14f8a9e3ea3e874a647835dc9e4a41ab58aaf848980 @@ -0,0 +1,2 @@ +I"9

标题表示方法

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b3/24692a2892dd72dff75c3917bbddcc9d206127f179f4e0b6c2df8eac904534 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b3/24692a2892dd72dff75c3917bbddcc9d206127f179f4e0b6c2df8eac904534 new file mode 100644 index 0000000000..6b3b2abac2 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b3/24692a2892dd72dff75c3917bbddcc9d206127f179f4e0b6c2df8eac904534 @@ -0,0 +1,259 @@ +I"X#

Excel参数化之参数化运行

+ +

在testcase文件夹下面创建test_excel_case.py文件,代码如下:

+ +
from config.Conf import ConfigYaml
+from config import Conf
+import os
+from common.ExcelData import Data
+from utils.LogUtil import my_log
+from common import ExcelConfig
+from utils.RequestsUtil import Request
+import json
+import pytest
+from common import Base
+from utils.AssertUtil import AssertUtil
+import allure
+#1、初始化信息
+#1).初始化测试用例文件
+case_file = os.path.join(Conf.get_data_path(),ConfigYaml().get_excel_file())
+#2).测试用例sheet名称
+sheet_name = ConfigYaml().get_excel_sheet()
+#3).获取运行测试用例列表
+data_init = Data(case_file,sheet_name)
+run_list = data_init.get_run_data()
+
+#4).日志
+log = my_log()
+#初始化dataconfig
+data_key = ExcelConfig.DataConfig
+#2、测试用例方法,参数化运行
+#一个用例的执行
+class TestExcel:
+    #1、增加Pyest
+    #2、修改方法参数
+    #3、重构函数内容
+    #4、pytest.main
+
+    def run_api(self,url,method,params=None,header=None,cookie=None):
+        """
+        发送请求api
+        :return:
+        """
+        # 2).接口请求
+        request = Request()
+        # params 转义json
+        # 验证params有没有内容
+        if len(str(params).strip()) is not 0:
+            params = json.loads(params)
+        # method post/get
+        if str(method).lower() == "get":
+            # 2.增加Headers
+            res = request.get(url, json=params, headers=header, cookies=cookie)
+        elif str(method).lower() == "post":
+            res = request.post(url, json=params, headers=header, cookies=cookie)
+        else:
+            log.error("错误请求method: %s" % method)
+
+        return res
+
+    def run_pre(self,pre_case):
+        #初始化数据
+        pass
+        url = ConfigYaml().get_conf_url()+pre_case[data_key.url]
+        method = pre_case[data_key.method]
+        params = pre_case[data_key.params]
+        headers = pre_case[data_key.headers]
+        cookies = pre_case[data_key.cookies]
+
+        # 1.判断headers是否存在,json转义,无需
+        # if headers:
+        #     header = json.loads(headers)
+        # else:
+        #     header = headers
+        header = Base.json_parse(headers)
+        # 3.增加cookies
+        # if cookies:
+        #     cookie = json.loads(cookies)
+        # else:
+        #     cookie = cookies
+        cookie = Base.json_parse(cookies)
+        res = self.run_api(url,method,params,header)
+        print("前置用例执行:%s"%res)
+        return res
+
+#1).初始化信息,url,data
+
+    # 1、增加Pyest
+    @pytest.mark.parametrize("case",run_list)
+    # 2、修改方法参数
+    def test_run(self,case):
+        # 3、重构函数内容
+        #data_key = ExcelConfig.DataConfig
+        # run_list第1个用例,用例,key获取values
+        url = ConfigYaml().get_conf_url()+case[data_key.url]
+        print(url)
+        case_id = case[data_key.case_id]
+        case_model = case[data_key.case_model]
+        case_name = case[data_key.case_name]
+        pre_exec = case[data_key.pre_exec]
+        method = case[data_key.method]
+        params_type = case[data_key.params_type]
+        params = case[data_key.params]
+        expect_result = case[data_key.expect_result]
+        headers = case[data_key.headers]
+        cookies =case[data_key.cookies]
+        code = case[data_key.code]
+        db_verify = case[data_key.db_verify]
+
+
+
+        # 1、验证前置条件
+        if pre_exec:
+            pass
+        # 2、找到执行用例
+            # 前置测试用例
+            pre_case = data_init.get_case_pre(pre_exec)
+            print("前置条件信息为:%s"%pre_case)
+            pre_res = self.run_pre(pre_case)
+            headers,cookies = self.get_correlation(headers,cookies,pre_res)
+
+        header = Base.json_parse(headers)
+        cookie = Base.json_parse(cookies)
+        res = self.run_api(url, method, params, header,cookie)
+        print("测试用例执行:%s" % res)
+
+        #allure
+        #sheet名称  feature 一级标签
+        allure.dynamic.feature(sheet_name)
+        #模块   story 二级标签
+        allure.dynamic.story(case_model)
+        #用例ID+接口名称  title
+        allure.dynamic.title(case_id+case_name)
+        #请求URL  请求类型 期望结果 实际结果描述
+        desc = "<font color='red'>请求URL: </font> {}<Br/>" \
+               "<font color='red'>请求类型: </font>{}<Br/>" \
+               "<font color='red'>期望结果: </font>{}<Br/>" \
+               "<font color='red'>实际结果: </font>{}".format(url,method,expect_result,res)
+        allure.dynamic.description(desc)
+
+        #断言验证
+        #状态码,返回结果内容,数据库相关的结果的验证
+        #状态码
+        assert_util = AssertUtil()
+        assert_util.assert_code(int(res["code"]),int(code))
+        #返回结果内容
+        assert_util.assert_in_body(str(res["body"]),str(expect_result))
+        #数据库结果断言
+        Base.assert_db("db_1",res["body"],db_verify)
+
+
+        #1、初始化数据库
+        # from common.Base import init_db
+        # sql = init_db("db_1")
+        # #2、查询sql,excel定义好的
+        # db_res = sql.fetchone(db_verify)
+        # log.debug("数据库查询结果:{}".format(str(db_res)))
+        # #3、数据库的结果与接口返回的结果验证
+        # #获取数据库结果的key
+        # verify_list = list(dict(db_res).keys())
+        # #根据key获取数据库结果,接口结果
+        # for line in verify_list:
+        #     res_line = res["body"][line]
+        #     res_db_line = dict(db_res)[line]
+        # #验证
+        #     assert_util.assert_body(res_line,res_db_line)
+        # 1.判断headers是否存在,json转义,无需
+        # if headers:
+        #     header = json.loads(headers)
+        # else:
+        #     header = headers
+        # # 3.增加cookies
+        # if cookies:
+        #     cookie = json.loads(cookies)
+        # else:
+        #     cookie = cookies
+
+        # #2).接口请求
+        # request = Request()
+        # #params 转义json
+        # #验证params有没有内容
+        # if len(str(params).strip()) is not 0:
+        #     params = json.loads(params)
+        # #method post/get
+        # if str(method).lower()=="get":
+        #     # 2.增加Headers
+        #     res = request.get(url,json=params,headers = header,cookies=cookie)
+        # elif str(method).lower()=="post":
+        #     res = request.post(url, json=params,headers = header,cookies=cookie)
+        # else:
+        #     log.error("错误请求method: %s"%method)
+        # print(res)
+#TestExcel().test_run()
+
+    def get_correlation(self,headers,cookies,pre_res):
+        """
+        关联
+        :param headers:
+        :param cookies:
+        :param pre_res:
+        :return:
+        """
+        #验证是否有关联
+        headers_para,cookies_para = Base.params_find(headers,cookies)
+        #有关联,执行前置用例,获取结果
+        if len(headers_para):
+            headers_data = pre_res["body"][headers_para[0]]
+        #结果替换
+            headers = Base.res_sub(headers,headers_data)
+        if len(cookies_para):
+            cookies_data = pre_res["body"][cookies_para[0]]
+            # 结果替换
+            cookies = Base.res_sub(headers, cookies_data)
+        return headers,cookies
+if __name__ == '__main__':
+    #pass
+    report_path = Conf.get_report_path()+os.sep+"result"
+    report_html_path = Conf.get_report_path()+os.sep+"html"
+    pytest.main(["-s","test_excel_case.py","--alluredir",report_path])
+
+
+    #Base.allure_report("./report/result","./report/html")
+    #Base.send_mail(title="接口测试报告结果",content=report_html_path)
+    #固定headers请求
+    #1.判断headers是否存在,json转义,无需
+    #2.增加Headers
+    #3.增加cookies
+    #4.发送请求
+
+    #动态关联
+    #1、验证前置条件
+    #if pre_exec:
+    #pass
+    #2、找到执行用例
+    #3、发送请求,获取前置用例结果
+    #发送获取前置测试用例,用例结果
+    #数据初始化,get/post,重构
+    #4、替换Headers变量
+        #1、验证请求中是否${}$,返回${}$内容
+    # str1 = '{"Authorization": "JWT ${token}$"}'
+    # if "${" in str1:
+    #     print(str1)
+    # import re
+    # pattern = re.compile('\${(.*)}\$')
+    # re_res = pattern.findall(str1)
+    # print(re_res[0])
+    #     #2、根据内容token,查询 前置条件测试用例返回结果token = 值
+    # token = "123"
+    #     #3、根据变量结果内容,替换
+    # res = re.sub(pattern,token,str1)
+    # print(res)
+    #5、请求发送
+
+    #1、查询,公共方法
+    #2、替换,公共方法
+    #3、验证请求中是否${}$,返回${}$内容,公共方法
+    #4、关联方法
+
+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b5/0f26f28478912068a9c50450e07f3df7b174e5e752771a7dc6fcf3d55fc4c9 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b5/0f26f28478912068a9c50450e07f3df7b174e5e752771a7dc6fcf3d55fc4c9 new file mode 100644 index 0000000000..85903b9cde --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b5/0f26f28478912068a9c50450e07f3df7b174e5e752771a7dc6fcf3d55fc4c9 @@ -0,0 +1,23 @@ +I"P + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b5/3081c88583aa9c3dd968a18cd448188fc58c043f90eaaae8807a6239bc447c b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b5/3081c88583aa9c3dd968a18cd448188fc58c043f90eaaae8807a6239bc447c new file mode 100644 index 0000000000..0624b3e8c9 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b5/3081c88583aa9c3dd968a18cd448188fc58c043f90eaaae8807a6239bc447c @@ -0,0 +1,327 @@ +I"4

主要包括Yaml文件的语法规则,安装方法,Yaml的编写规则,读取方法,Yaml的封装,以及如何在config文件夹下面简历config.yaml配置文件以实现测试用例和测试数据分离。

+ +

配置文件Yaml

+

Yaml 介绍

+

Yaml 是一种所有编程语言可用的友好的数据序列化标准。语法和其他高阶语言类似,并且可以简单表达字 +典、列表和其他基本数据类型的形态。例如,我们登录接口地址,就可以写在配置文件中,方便后续调用。

+ +

语法规则如下:

+
    +
  1. 大小写敏感。
  2. +
  3. 使用缩进表示层级关系。
  4. +
  5. 使用空格键缩进,而非Tab键缩进
  6. +
  7. 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可。
  8. +
  9. 文件中的字符串不需要使用引号标注,但若字符串包含有特殊字符则需用引号标注;
  10. +
  11. 注释标识为#,注释只能单独占一行。
  12. +
+ +

Yaml的安装

+
pip install pyyaml -i https://mirror.aliyun.com/pypi/simple/
+
+ +

Yaml的编写

+

字典格式:键值对的形式,每个键值对占据一行,记得要在:后面加一个空格

+
name: "test_yml"
+result: "success"
+
+

列表格式:用-表示列表,后面跟着空格。

+
- "a"
+- "b"
+- "c"
+
+

输出结果如下:

+
{'name': 'test_yml', 'result': 'success'}
+['a', 'b', 'c']
+
+

字典嵌套字典: 下一级的内容要缩进一格再写

+
person1:
+ name: xiaoming
+ age: 18
+person2:
+ name: xiaohong
+ age: 20
+
+

输出结果如下:

+
{'person1': {'name': 'xiaoming', 'age': 18}, 'person2': {'name': 'xiaohong', 'age': 20}}
+
+

字典嵌套列表:下一级的内容要缩进一格再写

+
person1:
+ - xiaoming
+ - 18
+person2:
+ - xiaohong
+ - 20
+
+

输出结果如下:

+
{'person1': ['xiaoming', 18], 'person2': ['xiaohong', 20]}
+
+

列表嵌套列表:

+
-
+ - a
+ - b
+ - c
+- 
+ - 1
+ - 2
+ - 3
+
+

输出结果如下:

+
[['a', 'b', 'c'], [1, 2, 3]]
+
+

列表嵌套字典:

+
-
+ name: xiaoming
+ age: 18
+-
+ name: xiaohong
+ age: 20
+
+

输出结果如下:

+
[{'name': 'xiaoming', 'age': 18}, {'name': 'xiaohong', 'age': 20}]
+
+ +

Yaml的读取

+

读取单个文件:

+
import yaml
+with open("./data.yaml", "r", encoding="utf-8") as f: # 获取文件对象,f是文件句柄,encoding="utf-8"是指定编码格式,防止出现中文乱码
+    # load方法将yaml文件转换成字典或列表
+    data = yaml.load(f, Loader=yaml.FullLoader)
+    # safe_load和load方法一样,但是不支持python的所有数据类型
+    print(data)
+
+

读取多个文件:这个主要用在比如所多个测试用例的数据都放在一个文件下。

+
import yaml 
+with open("./data.yaml", "r", encoding="utf-8") as f:
+    # load_all方法将yaml文件转换成生成器对象,每次读取一个文档,每个文档是一个字典或列表.
+    data = yaml.load_all(f, Loader=yaml.FullLoader) # Loader=yaml.FullLoader是指定加载器,防止出现警告
+    for i in data:
+        print(i)
+
+

对应的yaml文件如下:

+
---
+"username1": "test123"
+"password": "123456"
+---
+"username1": "test456"
+"password": "123456"
+
+

输出结果如下:

+
{'username1': 'test123', 'password': '123456'}
+{'username1': 'test456', 'password': '123456'}
+
+ +

配置文件-Yaml封装

+

yaml封装 → 配置文件conf.yaml → 基本目录配置 → 配置文件读取及使用 +
+在utils文件夹下面新建一个YamlUtil.py文件,用来封装yaml文件的读取操作。

+
import yaml
+import os
+# set up the class to read the yaml file
+class YamlReader:
+    def __init__(self, yamlf):
+        # 判断文件是否存在
+        if os.path.exists(yamlf):
+            self.yamlf = yamlf
+        else:
+            raise FileNotFoundError("文件不存在!")
+        self._data = None # 定义一个私有属性,用来存储读取的数据
+    # @property装饰器的作用是把一个方法变成属性调用
+    @property
+    def data(self):
+        # read data from yaml file
+        # 判断是否已经读取过数据,如果已经读取过数据,此时self._data为字典,不为None,就不需要再次读取数据。
+        if not self._data:
+            with open(self.yamlf, 'rb') as f: # rb是以二进制读取
+                self._data = yaml.safe_load(f)
+        return self._data # 返回读取的数据
+
+

测试一下这个util文件是否可以正常读取yaml文件,在t_yaml文件夹下面新建一个yaml_demo.py文件,用来测试YamlUtil.py文件是否可以正常读取yaml文件。

+
from utils.YamlUtil import YamlReader
+yaml_path = "./data.yml"
+data = YamlReader(yaml_path).data
+print(data)
+
+

yaml文件内容如下:

+
name: "test_yml"
+result: "success"
+
+

输出结果如下:

+
{'name': 'test_yml', 'result': 'success'}
+
+ +

以上封装的YamlUtil.py文件只能读取单个yaml文件,如果要读取多个yaml文件,需要在YamlUtil.py文件中添加一个方法,用来读取多个yaml文件。 +
+以上的YamlUtil.py模块在读取下列多个yaml文件时候的报错信息如下: +yaml文件内容:

+
---
+"username1": "test123"
+"password": "123456"
+---
+"username1": "test456"
+"password": "123456"
+
+

报错信息如下:

+
raise ComposerError("expected a single document in the stream",
+yaml.composer.ComposerError: expected a single document in the stream
+  in "./data.yaml", line *, column 1
+
+ +

在YamlUtil.py文件中添加一个方法,用来读取多个yaml文件:

+
import yaml
+import os
+# set up the class to read the yaml file
+class YamlReader:
+    def __init__(self, yamlf):
+        # 判断文件是否存在
+        if os.path.exists(yamlf):
+            self.yamlf = yamlf
+        else:
+            raise FileNotFoundError("文件不存在!")
+        self._data = None # 定义一个私有属性,用来存储读取的数据
+        self._data_all = None # 定义一个私有属性,用来存储读取的数据
+    # @property装饰器的作用是把一个方法变成属性调用
+    @property
+    def data(self):
+        # read data from yaml file
+        # 判断是否已经读取过数据,如果已经读取过数据,此时self._data为字典,不为None,就不需要再次读取数据。
+        if not self._data:
+            with open(self.yamlf, 'rb') as f: # rb是以二进制读取
+                self._data = yaml.safe_load(f)
+        return self._data # 返回读取的数据
+    # 读取多个yaml文件
+    def data_all(self):
+        # read data from yaml file
+        # 判断是否已经读取过数据,如果已经读取过数据,此时self._data为字典,不为None,就不需要再次读取数据。
+        if not self._data_all:
+            with open(self.yamlf, 'rb') as f: # rb是以二进制读取
+                self._data_all = list(yaml.safe_load_all(f)) # list()函数将yaml.safe_load_all(f)转换为列表
+        return self._data_all # 返回读取的数据
+
+

yaml文件内容:

+
---
+"username1": "test123"
+"password": "123456"
+---
+"username1": "test456"
+"password": "123456"
+
+

yaml_demo.py文件内容:

+
from utils.YamlUtil import YamlReader
+yaml_path = "./data.yaml"
+data = YamlReader(yaml_path).data_all()
+print(data)
+
+

输出结果如下:

+
[{'username1': 'test123', 'password': '123456'}, {'username1': 'test456', 'password': '123456'}]
+
+

Yaml配置文件设置

+

在config文件下面,新建一个yaml文件,命名为config.yaml,文件内容如下:

+
# 测试环境
+BASE:
+  test:
+    url: "http://211.103.136.242:8064"
+
+

在config文件下面,新建一个py文件,命名为config.py,用来读取配置文件config.yaml中的内容,文件内容如下: +首先是,获取当前目录

+
# 1. 获取当前基本目录
+import os
+current = os.path.abspath(__file__)# __file__表示当前文件,这步操作是获取当前文件的绝对路径
+# /home/hugo/PycharmProjects/InterAutoTest_W/venv/bin/python /home/hugo/PycharmProjects/InterAutoTest_W/config/config.py 
+tmp_dir = os.path.dirname(current)# 获取当前文件的父目录
+# /home/hugo/PycharmProjects/InterAutoTest_W/venv/bin/python /home/hugo/PycharmProjects/InterAutoTest_W/config
+# 获取config目录的父路径
+base_dir = os.path.dirname(tmp_dir) 
+# /home/hugo/PycharmProjects/InterAutoTest_W/venv/bin/python /home/hugo/PycharmProjects/InterAutoTest_W
+# 定义config目录的路径
+_config_dir = os.path.join(base_dir, "config") # _开头的变量表示私有变量
+# /home/hugo/PycharmProjects/InterAutoTest_W/venv/bin/python /home/hugo/PycharmProjects/InterAutoTest_W/config
+# 或者也可以这样写:
+# _config_dir = base_dir + os.sep + "config"
+# 定义config.yaml文件的路径
+_config_yaml = os.path.join(_config_dir, "config.yaml")
+
+def get_config_path():
+    return _config_dir
+def get_config_file():
+    return _config_yaml
+
+ +

然后是,读取配置文件config.yaml中的内容

+
from utils.YamlUtil import YamlReader
+# 1. 获取当前基本目录
+import os
+current = os.path.abspath(__file__)# __file__表示当前文件,这步操作是获取当前文件的绝对路径
+# /home/hugo/PycharmProjects/InterAutoTest_W/venv/bin/python /home/hugo/PycharmProjects/InterAutoTest_W/config/config.py 
+tmp_dir = os.path.dirname(current)# 获取当前文件的父目录
+# /home/hugo/PycharmProjects/InterAutoTest_W/venv/bin/python /home/hugo/PycharmProjects/InterAutoTest_W/config
+# 获取config目录的父路径
+base_dir = os.path.dirname(tmp_dir) 
+# /home/hugo/PycharmProjects/InterAutoTest_W/venv/bin/python /home/hugo/PycharmProjects/InterAutoTest_W
+# 定义config目录的路径
+_config_dir = os.path.join(base_dir, "config") # _开头的变量表示私有变量
+# /home/hugo/PycharmProjects/InterAutoTest_W/venv/bin/python /home/hugo/PycharmProjects/InterAutoTest_W/config
+# 或者也可以这样写:
+# _config_dir = base_dir + os.sep + "config"
+# 定义config.yaml文件的路径
+_config_yaml = os.path.join(_config_dir, "config.yaml")
+
+def get_config_path():
+    return _config_dir
+def get_config_file():
+    return _config_yaml # _下划线开头的变量表示私有变量
+
+# 2. 读取配置文件
+class ConfigYaml:
+    # 初始化yaml读取配置文件
+    def __init__(self):
+        self.config = YamlReader(get_config_file()).data # self.表示当前类的实例
+    # 获取url
+    def get_conf_url(self):
+        return self.config['BASE']['test']['url']
+if __name__ == '__main__':
+    conf_read = ConfigYaml()
+    print(conf_read.get_conf_url())
+
+

这一步做完之后,回到测试用例文件test_mall.py,在这个测试用例,调用config.py文件中的内容,可以实现测试数据和测试用例的分离。 +
+例如,原来的登录测试代码如下:

+
from utils.RequestsUtils import requests_get, requests_post
+
+def login():
+# define the testing data
+
+    url = "http://211.103.136.242:8064/autuorizations/"
+    data ={"username":"python","password":"12345678"}
+    # # sent the POST request
+    # response = requests.post(url=url,json=data)
+    # # print the response
+    # print(response.json())
+
+    # 使用utils封装的方法
+    response = requests_post(url=url,json=data,headers=None)
+    # print the response
+    print(response)
+
+ +

后来的代码如下:

+
from utils.RequestsUtils import requests_get, requests_post
+from config.config import ConfigYaml
+
+# 定义测试的url
+conf_y = ConfigYaml()
+url_path = conf_y.get_conf_url()
+## define login in function
+
+def login():
+    # define the testing data
+
+    url = url_path +"/autuorizations/"
+    # url = "http://211.103.136.242:8064/autuorizations/"
+    data = {"username": "python", "password": "12345678"}
+
+    # 使用utils封装的方法
+    response = requests_post(url=url, json=data, headers=None)
+    # print the response
+    print(response)
+
+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b5/63e4ebfb45e034659a343d1418930dbeb05b4ac28468e767925df854405087 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b5/63e4ebfb45e034659a343d1418930dbeb05b4ac28468e767925df854405087 new file mode 100644 index 0000000000..e7b0607ec4 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b5/63e4ebfb45e034659a343d1418930dbeb05b4ac28468e767925df854405087 @@ -0,0 +1,5 @@ +I"

主要包括python一些不太常见的知识点以及python高阶编程需要用到的知识点等等。 +比如说*args 和 **kwargs在函数传参以及函数调用中的用法,以及python中的装饰器等等。 +还比如说pdb在程序运行时查看变量的值,查看程序的执行流程,以及查看函数的调用栈。 +还例如,python中的Map、Reduce、Filter、Lambda等在函数式编程中的用法。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b7/4d23b02c0db31e3567f0a12bd887c6f4abc524cb135dbb58391c9345d0d75e b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b7/4d23b02c0db31e3567f0a12bd887c6f4abc524cb135dbb58391c9345d0d75e new file mode 100644 index 0000000000..2d5cc659cc Binary files /dev/null and b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b7/4d23b02c0db31e3567f0a12bd887c6f4abc524cb135dbb58391c9345d0d75e differ diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b7/a9c67d334754d36bfa089ae7a409dd1ff69e77eb1a136d0365235962e30610 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b7/a9c67d334754d36bfa089ae7a409dd1ff69e77eb1a136d0365235962e30610 new file mode 100644 index 0000000000..d8485ff3d0 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/b7/a9c67d334754d36bfa089ae7a409dd1ff69e77eb1a136d0365235962e30610 @@ -0,0 +1,71 @@ +I"

日志文件备份

+ +

有时候,为了系统稳定性,除了在/var/log通过回滚机制生成各种日志文件以排查系统故障之外,我们也想把这些关键的日志文件保存到指定文件夹,防止突然断电或者磁盘分区被破坏之后丢失关键的日志信息。

+ +

代码如下:(文件名为backup_sysconfig.sh)

+ +
#! /bin/bash
+# this script is used to backup the syslog file in /var/log dir everyday
+echo `date`
+cp /var/log/syslog /home/hugo/test_BASH/log
+# adding the user with x permission
+chmod u+x syslog
+# chnage the firname
+today=$(date +%y%m%d)
+echo $today
+mv ./log/syslog ./log/syslog_$today # 同一个文件夹下面就相当于重命名
+
+ +

定时调度任务

+

crontab 的语句是“分 时 日 月 周 命令”.

+ +

开启定时调度任务,每天凌晨1点执行一次

+ +
crontab -e
+# 0 1 * * * /home/hugo/test_BASH/back_sysconfig.sh
+
+

显示crontab: installing new crontab,表示定时任务已经开启。

+ +

查看定时任务

+ +
crontab -l
+
+ +

开启或者关闭定时任务

+ +
service crond start
+service crond stop
+
+ +

发现报错:Failed to start crond.service: Unit crond.service not found.

+ +

删除调度任务:

+ + + +
# Unlike any other crontab you don't have to run the `crontab'
+# command to install the new version when you edit this file
+# and files in /etc/cron.d. These files also have username fields,
+# that none of the other crontabs do.
+
+SHELL=/bin/sh
+# You can also override PATH, but by default, newer versions inherit it from the environment
+#PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+
+# Example of job definition:
+# .---------------- minute (0 - 59)
+# |  .------------- hour (0 - 23)
+# |  |  .---------- day of month (1 - 31)
+# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
+# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
+# |  |  |  |  |
+# *  *  *  *  * user-name command to be executed
+17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
+25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
+47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
+52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
+
+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/bb/24a3cfee922a3c35cda7cc183e79cfc0e50c8ccb5d2415f3e70f2d3b31e9ac b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/bb/24a3cfee922a3c35cda7cc183e79cfc0e50c8ccb5d2415f3e70f2d3b31e9ac new file mode 100644 index 0000000000..5e12360db3 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/bb/24a3cfee922a3c35cda7cc183e79cfc0e50c8ccb5d2415f3e70f2d3b31e9ac @@ -0,0 +1,330 @@ +I"1

包括yaml和Excel用例测试和数据驱动开发。

+ +

数据驱动的自动化测试

+ +

什么是数据驱动

+ +

数据驱动是指将测试数据与测试代码分离,将测试数据存储在一个独立的文件中,然后在测试代码中读取测试数据,最后将测试数据作为参数传递给测试代码。这样做的好处是,测试数据和测试代码分离,测试数据可以独立于测试代码进行修改,测试代码可以独立于测试数据进行修改,测试数据可以被多个测试用例共享,测试数据可以被多个测试框架共享。

+ +

yaml数据驱动

+ +

yaml是一种数据序列化格式,可以用来存储数据,也可以用来存储配置信息。yaml的语法比较简单,可以用来存储简单的数据结构,比如列表、字典、字符串、数字等。

+ +

首先,在data文件夹下面创建所需要用到的测试数据文件,比如testlogin.yaml,然后在login_data.yaml文件中存储测试数据,比如:

+ +

+#登录测试用例
+# ---用来分割不同的测试用例
+---
+#测试用例名称
+"case_name": "登录成功用例"
+#url地址
+"url": "/authorizations/"
+#data
+"data":
+  username: "python"
+  password: "12345678"
+#期望结果
+"expect": '"user_id": 1, "username": "python"'
+---
+#测试用例名称
+"case_name": "登录失败用例"
+#url地址
+"url": "/authorizations/"
+#data
+"data":
+  username: "test123456"
+  password: "111111"
+#期望结果
+"expect": '"user_id": 1, "username": "python"'
+
+ +

在这个测试数据着哦功能,当输入用户名和密码正确时,登录成功,当输入用户名和密码错误时,登录失败。

+ +

接下来,在测试代码中读取测试数据,执行测试用例,然后将测试数据作为参数传递给测试代码(也即是参数化),比如:在testcases文件夹下面创建test_login.py文件,然后在test_login.py文件中编写测试代码.

+ +

这个测试代码应该包括以下内容:

+
    +
  1. 获取测试用例内容,包括获取testlogin.yaml文件的文件路径,使用工具类来读取多个文档内容。 +
      +
    1. 获取testlogin.yaml文件的文件路径:在config文件夹下面的Conf.py文件中添加获取testlogin.yaml文件的文件路径的方法,比如: +
      import os
      +from utils.YamlUtil import YamlReader
      +#1、获取项目基本目录
      +#获取当前项目的绝对路径
      +current = os.path.abspath(__file__)
      +#print(current)
      +BASE_DIR = os.path.dirname(os.path.dirname(current))
      +#定义data目录的路径
      +_data_path = BASE_DIR + os.sep + "data" # _开头的变量表示私有变量
      +def get_data_path():
      +   return _data_path
      +
      +
    2. +
    +
  2. +
  3. 参数化执行测试用例,使用pytest.mark.parametrize来实现参数化。
  4. +
+ +

test_login.py文件代码如下:

+ +
from config import Conf
+import os
+from utils.YamlUtil import YamlReader
+import pytest
+from config.Conf import ConfigYaml
+from utils.RequestsUtil import Request
+#1、获取测试用例内容list
+#获取testlogin.yml文件路径
+test_file = os.path.join(Conf.get_data_path(),"testlogin.yml")
+#print(test_file)
+#使用工具类来读取多个文档内容
+data_list = YamlReader(test_file).data_all()
+print(data_list)
+#2、参数化执行测试用例
+
+@pytest.mark.parametrize("login",data_list)
+def test_yaml(login):
+    #初始化url,data
+    url = ConfigYaml().get_conf_url()+login["url"]
+    print("url %s"%url)
+    data = login["data"]
+    print("data %s"%data)
+    #post请求
+    request = Request()
+    res  = request.post(url,json=data)
+    #打印结果
+    print(res)
+
+if __name__ == "__main__":
+    pytest.main(["-s","Test_login.py"])
+
+
+ +

到这里,我们已经完成了测试数据的编写,以及测试代码文档的编写,然后我们编写了使用登录的测试例,通过测试用例参数化去获取这个列表,最后我们还需要对得到的结果进行断言,断言的结果是否和我们期望的结果一致,如果一致,那么就是测试通过,如果不一致,那么就是测试失败。

+ +

Excel数据驱动开发

+ +

在上面的测试用例中,我们使用了yaml文件来存储测试数据,但是yaml文件的可读性不是很好,所以我们可以使用Excel来存储测试数据,然后使用python来读取Excel中的数据,然后再进行参数化。

+ +

这一节内容主要包括Excel用例设计,Excel读取以及Excel参数化运行等等。

+ +

Excel用例设计

+ +

EXCEL用例设计非常重要,要充分全面的覆盖到测试用例的各个方面,比如:测试用例编号、测试用例名称、测试用例描述、测试用例前置条件、测试用例步骤、测试用例预期结果、测试用例实际结果、测试用例执行结果等等。

+ +

Excel用例设计

+ +

Excel读取

+ +

在testcase文件夹下面创建文件夹t_excel,然后在t_excel文件夹下面创建文件excel_demo.py,然后在excel_demo.py文件中编写代码,代码如下:

+ +

在这里要注意xlrd读取excel文件的具体报错。

+ +

xlrd==1.2.0,报错信息如下:(在新版python3.10中,更新删除了getiterator方法,所以我们老版本的xlrd库调用getiterator方法时会报错。)

+
AttributeError: 'ElementTree' object has no attribute 'getiterator'
+
+

解决方法: +通过pip show xlrd命令,找出目录python\Lib\site-packages\xlrd下的xlsx.py文件

+ +

修改两个地方的的getiterator()改成iter(),下面的两个地方,这里已经把getiterator()改成iter()了

+ +
#1、导入包,xlrd
+import xlrd
+#2、创建workbook对象,这里先把excel文件拷贝一份到当前目录下
+book = xlrd.open_workbook("testdata.xlsx")
+#3、sheet对象
+#索引
+#sheet = book.sheet_by_index(0)
+#名称
+sheet = book.sheet_by_name("美多商城接口测试")
+#4、获取行数和列数
+rows = sheet.nrows #行数
+cols = sheet.ncols #列数
+#5、读取每行的内容
+for r in range(rows):
+    r_values = sheet.row_values(r)
+    #print(r_values)
+#6、读取每列的内容
+for c in range(cols):
+    c_values = sheet.col_values(c)
+    #print(c_values)
+#7、读取固定列的内容
+print(sheet.cell(1,1))
+
+
+ +

Excel参数化运行

+ +

在utils文件夹下面创建ExcelUtil.py文件,然后在ExcelUtil.py文件中编写代码,主要用于参数化,代码如下:

+ +
import os
+import xlrd
+
+#目的:参数化,pytest 需要返回的数据用list来封装
+#自定义异常
+class SheetTypeError:
+    pass
+#1、验证文件是否存在,存在读取,不存在报错
+class ExcelReader:
+    def __init__(self,excel_file,sheet_by):
+        if os.path.exists(excel_file):
+            self.excel_file = excel_file
+            self.sheet_by = sheet_by
+            self._data=list()
+        else:
+            raise  FileNotFoundError("文件不存在")
+#2、读取sheet方式,名称,索引
+    def data(self):
+        #存在不读取,不存在读取
+        if not self._data:
+            workbook = xlrd.open_workbook(self.excel_file)
+            if type(self.sheet_by) not in [str,int]:
+                raise SheetTypeError("请输入Int or Str")
+            elif type(self.sheet_by) == int:
+                sheet = workbook.sheet_by_index(self.sheet_by)
+            elif type(self.sheet_by) == str:
+                sheet = workbook.sheet_by_name(self.sheet_by)
+    #3、读取sheet内容
+            #返回list,元素:字典
+            #格式[{"a":"a1","b":"b1"},{"a":"a2","b":"b2"}]
+            #1.获取首行的信息
+            title = sheet.row_values(0)
+            #2.遍历测试行,与首行组成dict,放在list
+                #1 循环,过滤首行,从1开始
+            for col in range(1,sheet.nrows):
+                col_value = sheet.row_values(col)
+                #2 与首组成字典,放list
+                self._data.append(dict(zip(title, col_value)))
+
+#4、结果返回
+        return self._data
+
+# head = ["a","b"]
+# value1 = ["a1","b1"]
+# value2 =  ["a2","b2"]
+# data_list= list()
+# #zip
+# data_list.append(dict(zip(head,value1)))
+# data_list.append(dict(zip(head,value2)))
+# #print(dict(zip(head,value1)))
+# #print(dict(zip(head,value2)))
+# print(data_list)
+
+if __name__ == "__main__":
+    reader = ExcelReader("../data/testdata.xlsx","美多商城接口测试")
+    print(reader.data())
+
+ +

然后,获取是否运行,参数化,并进行结果断言。

+ +

获取是否运行

+ +

获取是否运行:在common文件夹下面创建ExcelData.py文件,然后在ExcelData.py文件中编写代码,代码如下:

+ +
from utils.ExcelUtil import ExcelReader
+from common.ExcelConfig import DataConfig
+
+class Data:
+    def __init__(self,testcase_file,sheet_name):
+    #1、使用excel工具类,获取结果list
+        #self.reader = ExcelReader("../data/testdata.xlsx","美多商城接口测试")
+        self.reader = ExcelReader(testcase_file,sheet_name)
+        #print(reader.data())
+    #2、列是否运行内容,y
+    def get_run_data(self):
+        """
+        根据是否运行列==y,获取执行测试用例
+        :return:
+        """
+        run_list = list()
+        for line in self.reader.data():
+            if str(line[DataConfig().is_run]).lower() == "y":
+                #print(line)
+        #3、保存要执行结果,放到新的列表。
+                run_list.append(line)
+        print(run_list)
+        return run_list
+
+    def get_case_list(self):
+        """
+        获取全部测试用例
+        :return:
+        """
+        # run_list=list()
+        # for line in self.reader.data():
+        #         run_list.append(line)
+        run_list = [ line for line in self.reader.data()]
+        return run_list
+
+    def get_case_pre(self,pre):
+        #获取全部测试用例
+        #list判断,执行,获取
+        """
+        根据前置条件:从全部测试用例取到测试用例
+        :param pre:
+        :return:
+        """
+        run_list = self.get_case_list()
+        for line in run_list:
+            if pre in dict(line).values():
+                return line
+        return None
+
+ +

其中,我们这个是根据是否运行列==y,获取执行测试用例,还需要创建一个ExcelConfig.py文件,来表示对应的映射关系。代码如下:

+ +

+
+#定义类
+class DataConfig:
+#定义列属性
+    #用例ID	模块	接口名称	请求URL	前置条件	请求类型	请求参数类型
+    #请求参数	预期结果	实际结果	备注	是否运行	headers	cookies	status_code	数据库验证
+    #用例ID
+    case_id = "用例ID"
+    case_model = "模块"
+    case_name = "接口名称"
+    url = "请求URL"
+    pre_exec = "前置条件"
+    method = "请求类型"
+    params_type = "请求参数类型"
+    params = "请求参数"
+    expect_result = "预期结果"
+    actual_result = "实际结果"
+    is_run = "是否运行"
+    headers = "headers"
+    cookies = "cookies"
+    code = "status_code"
+    db_verify = "数据库验证"
+
+
+ +

我们还需要获取testcase_file,sheet_name,所以我们需要修改config文件夹下面的config.yml,代码如下:

+ +
BASE:
+ test:
+  url: "http://211.103.136.242:8064"
+  case_file: "testdata.xlsx"
+  case_sheet: "美多商城接口测试"
+
+ +

改完之后,需要修改Conf.py文件,获取测试用例sheet名称,代码如下:

+ +
class ConfigYaml:
+    #定义方法获取需要信息
+    def get_excel_file(self):
+        """
+        获取测试用例excel名称
+        :return:
+        """
+        return self.config["BASE"]["test"]["case_file"]
+
+    def get_excel_sheet(self):
+        """
+        获取测试用例sheet名称
+        :return:
+        """
+        return self.config["BASE"]["test"]["case_sheet"]
+
+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/bb/3016622bd0b0892951e1d1abc4923518588a6791585e633eb5ba50c3eefb41 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/bb/3016622bd0b0892951e1d1abc4923518588a6791585e633eb5ba50c3eefb41 new file mode 100644 index 0000000000..f696dfd69c --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/bb/3016622bd0b0892951e1d1abc4923518588a6791585e633eb5ba50c3eefb41 @@ -0,0 +1,9 @@ +I"| + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/bb/3d642272ddd172760c586a4d9dd0cc33f67dcd173405e5d1d782682d9b73d2 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/bb/3d642272ddd172760c586a4d9dd0cc33f67dcd173405e5d1d782682d9b73d2 new file mode 100644 index 0000000000..c71aa688e4 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/bb/3d642272ddd172760c586a4d9dd0cc33f67dcd173405e5d1d782682d9b73d2 @@ -0,0 +1,209 @@ +I"

主要包括requests库的安装,接口用例编写,requests库的使用,requests库的返回值介绍,requests库的用例编写以及Requests的封装等等。

+ +

接口用例编写

+

实际情况下,每个模块的测试用例写的越详细越好,要根据接口文档进行编写。

+ +

png

+ +

安装项目依赖

+

下载requirements.txt中所需的package:

+
pip install -r requirements.txt
+
+

列出已经安装的包裹

+
pip list
+
+ +

pip install临时添加镜像源:

+
pip install pytest -i https://pypi.tuna.tsinghua.edu.cn/simple 
+
+

国内镜像源包括:

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
镜像源地址
清华https://pypi.tuna.tsinghua.edu.cn/simple
阿里http://mirrors.aliyun.com/pypi/simple/
豆瓣http://pypi.douban.com/simple/
百度https://mirror.baidu.com/pypi/simple
+ +

Requests

+

Requests是一个Python HTTP库,用于发送HTTP请求。它比urllib更加方便,但是它不支持验证、cookies或者其它复杂的HTTP客户端功能。Requests是一个Apache2 Licensed开源项目,由Kenneth Reitz创建和维护。

+

安装

+
pip install requests
+
+

使用

+
import requests
+r = requests.get('https://api.github.com/events')
+print(r.text)
+
+

请求返回介绍

+
r.status_code #响应状态
+r.content #字节方式的响应体,会自动为你解码 gzip 和 deflate 压缩
+r.headers #以字典对象存储服务器响应头,若键不存在则返回None
+r.json() #Requests中内置的JSON
+r.url # 获取url
+r.encoding # 编码格式
+r.cookies # 获取cookie
+r.raw #返回原始响应体
+r.text #字符串方式的响应体,会自动根据响应头部的字符编码进行
+r.raise_for_status() #失败请求(非200响应)抛出异常
+
+

Requests用例编写

+ +

编写登录脚本

+
import requests
+def login():
+    url = "http://172.17.0.139:5004/authorizations/"
+    data = {"username":"python",
+    "password":"12345678"}
+    r= requests.post(url,json=data)
+    print(r.text)
+
+

编写个人信息获取脚本

+ +
import requests
+def info():
+    url = "http://172.17.0.139:5004/user/"
+    token = "***"
+    headers = {
+                "Authorization": "JWT"+token
+            }
+    r = requests.get(url,headers=headers)
+    print(r.text)
+
+ +

编写商品列表脚本

+
import requests
+def goods_list():
+    url = "http://172.17.0.139:5004/categories/115/skus"
+    data = {
+            # "categroy_id":"115",
+            "page":"1",
+            "page_size": "10",
+            "ordering": "create_time",#'create_time', 'price', 'sales'
+            }
+    r = requests.get(url, json=data)
+    print(r.text)
+    print(r.json)
+
+

添加到购物车

+
import requests
+def cart():
+    url = "http://172.17.0.139:5004/cart/"
+    data = {
+            "sku_id": "3",
+            "count": "1",
+            "selected": "true",
+            }
+    token = "***"
+    headers = {
+                "Authorization": "JWT"+token
+            }
+    r = requests.post(url, json=data,headers=headers)
+    print(r.text)
+
+

保存订单

+
import requests
+def order():
+    url = "http://172.17.0.139:5004/cart/"
+    token = "***"
+    headers = {
+                "Authorization": "JWT"+token
+                }
+    r = requests.get(url,headers=headers)
+    print(r.text)
+
+ +

Requests的封装

+

封装requests_get方法

+
#1、创建封装get方法
+def requests_get(url,headers):
+    #2、发送requests get请求
+    r = requests.get(url,headers = headers)
+    #3、获取结果相应内容
+    code = r.status_code
+    try:
+    body = r.json()
+    except Exception as e:
+    body = r.text
+    #4、内容存到字典
+    res = dict()
+    res["code"] = code
+    res["body"] = body
+    #5、字典返回
+    return res
+
+

requests_post方法封装

+
#1、创建post方法
+def requests_post(url,json=None,headers=None):
+    #2、发送post请求
+    r= requests.post(url,json=json,headers=headers)
+    #3、获取结果内容
+    code = r.status_code
+    try:
+        body = r.json()
+    except Exception as e:
+        body = r.text
+    #4、内容存到字典
+    res = dict()
+    res["code"] = code
+    res["body"] = body
+    #5、字典返回
+    return res
+
+ +

可以发现,requests_get和requests_post方法的区别就是请求方式不同,其他都是一样的,所以我们可以把这两个方法合并成一个方法,这样就可以减少代码量,提高代码的复用性.

+
# 重构
+# 1、创建类
+class Requests:
+# 2、定义公共方法
+# 2.1 增加方法的参数,根据参数来验证请求方式是get还是post
+# 2.2 重复的内容,复制进来即可
+# 后续参数还可以加入data、cookies等
+  def requests_api(self,url,json=None,headers=None,method="get"):
+    # 3、判断请求方式
+    if method == "get":
+      r = requests.get(url,headers=headers)
+    elif method == "post":
+      r = requests.post(url,json=json,headers=headers)
+    # 4、获取结果内容
+    code = r.status_code
+    try:
+      body = r.json()
+    except Exception as e:
+      body = r.text
+    # 5、内容存到字典
+    res = dict()
+    res["code"] = code
+    res["body"] = body
+    # 6、字典返回
+    return res
+### 重构get/post方法
+  def get(self,**kwargs):
+    # 调用公共方法
+    return self.requests_api(url,method="get",**kwargs)
+  def post(self,**kwargs):
+    # 调用公共方法
+    return self.requests_api(url,method="post",**kwargs)
+
+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/bd/f1b3223fbed6ccf28daefebd00c001e5d7b0f49aeba74c2139ba7368cfa5cb b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/bd/f1b3223fbed6ccf28daefebd00c001e5d7b0f49aeba74c2139ba7368cfa5cb new file mode 100644 index 0000000000..266c5c73e4 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/bd/f1b3223fbed6ccf28daefebd00c001e5d7b0f49aeba74c2139ba7368cfa5cb @@ -0,0 +1,105 @@ +I"

谢春花的歌

+ +

https://link.jscdn.cn/sharepoint/aHR0cHM6Ly9oa3VzdGNvbm5lY3QtbXkuc2hhcmVwb2ludC5jb20vOnU6L2cvcGVyc29uYWwvamNoZW5mbF9jb25uZWN0X3VzdF9oay9FVThfNm53Y1ZSQkVtc1FCdzY1WDJnNEJmcnhjaXBrSDdKZHZvWWg2R1Z0ekR3P2U9QUh6cWhP.mp3

+ +

什么是locust

+ +

locust是一个简单易用的分布式负载测试工具,主要用来对网站进行负载压力测试。locust使用python语言开发,测试资源消耗远远小于java语言开发的jmeter。且其支持分布式部署测试,能够轻松模拟百万级用户并发测试。

+ +

什么是性能测试

+ +

性能测试目的主要是解决三个问题:

+ + + +

这三个问题需要从测试的结果中分析得出,其中最重要的性能指标有三点:

+ +

(1)响应时间

+ +

(2)吞吐量

+ +

(3)成功率

+ +

传统的性能测试工具有很多, 比如 **Apache Bench、JMeter、LoadRunner **等等,它们都能用来持续增加压力。

+ +

不过,Apache Bench 之类的专用工具用来做简单的 HTTP 接口测试还行,如果需要做业务接口的串联测试就力有未逮了。

+ +

**JMeter **当然功能强大,也有一定的扩展性,但在这节课我并不想用 JMeter , 原因有两点,一是因为 JMeter 是资源消耗黑洞, 每个任务 / 用户都要使用一个线程,二是因为 JMeter 是基于配置的。

+ +

相比之下,新一代的性能测试工具 ** Locust** (蝗虫)是基于编程来实现测试用例的性能测试工具,它更加灵活。而且,它使用 Python 代码来定义用户行为,用写 Python 代码的方式来写测试脚本,远离了复杂的配置脚本和图形界面,利用强大而丰富的 Python 类库可以轻松支持各种协议,简单易用,扩展方便。

+ +

locust优缺点

+ +

优点:

+ +

1.不同与wrk和jmeter使用线程数提高并发量,locust借助于协程实现对用户的模拟,相同物理资源(机器cpu、内存等)配置下locust能支持的并发用户数相比jmeter可以提升一个数量级 +2.相比wrk对复杂场景测试的捉襟见肘和jmeter需要界面点击录制复杂场景的麻烦,locust只需用户使用python编写用户场景完成测试 +3.不同与jmeter复杂的用户使用界面,locust的界面干净整洁,可以实时显示测试的相关细节(如发送请求数、失败数和当前发送请求速度等) +4.locust虽然是面向web应用测试的,但是它可以用来测试几乎所有系统。给locust编写一个客户端,可以满足你所有的测试要求

+ +

缺点:

+ +

1.同wrk一样,locust测试结果输出不如jmeter的测试结果展示类型多

+ +

运行

+ +

运行脚本如下:

+ +
from config.config_read import read_yml_file, read_test_data
+from locust import HttpUser, task
+
+headers = read_yml_file('./config/config.yml')['headers']
+params = read_test_data('./config/test_data_process.yml', 'findByPidAndStatusActive')[0]
+
+
+
+class Quickstart(HttpUser):
+    min_wait = 100  # 最小等待时间(ms),模拟用户在执行每个任务之间等待的最小时间
+    max_wait = 500  # 最大等待时长(ms),模拟用户在执行每个任务之间等待的最大时长
+    host = read_yml_file('./config/config.yml')['baseurl'] # 访问的域名
+
+    def on_start(self):
+        #开始任务
+        print("start working")
+
+    # 任务target,用@task标记
+    @task
+    def mytask(self):
+        self.client.get('/process/findByPidAndStatusActive', headers=headers, params=params)
+
+ +
    +
  1. 运行脚本方法:
  2. +
+ +
 locust -f ***.py
+
+ +

-f 指定脚本文件路径

+ +
    +
  1. 访问locust UI界面
  2. +
+ +

Starting web interface at http://0.0.0.0:8089 (accepting connections from all network interfaces)

+ +
    +
  1. +

    模拟用户访问

    + +

    指定模拟的用户数(Number of users),以及孵化率(Spawn rate,每秒增加的用户数), 以及所要测试的服务器地址(Host)。 +20230322172524

    +
  2. +
  3. +

    查看结果 +RPS(Request Per Second): 每秒请求数的变化,它反映了吞吐量 +Number of Users, 用户并发数量的变化, 它反映了系统能承受的并发请求吞吐量 + Response Time 响应时间的变化

    +
  4. +
+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/c1/001764edb9a020f7bd5c8c44ecdc54b5c493b97960cbd9b426dd598303e9fd b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/c1/001764edb9a020f7bd5c8c44ecdc54b5c493b97960cbd9b426dd598303e9fd new file mode 100644 index 0000000000..ad72f68115 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/c1/001764edb9a020f7bd5c8c44ecdc54b5c493b97960cbd9b426dd598303e9fd @@ -0,0 +1,2 @@ +I"2

1. 数据来源

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/c7/6a4190b85b5ae7c8d4ea84f08b33b8122c8f3bfc033140b435062cd711679f b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/c7/6a4190b85b5ae7c8d4ea84f08b33b8122c8f3bfc033140b435062cd711679f new file mode 100644 index 0000000000..7c9ad89e3c --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/c7/6a4190b85b5ae7c8d4ea84f08b33b8122c8f3bfc033140b435062cd711679f @@ -0,0 +1,2 @@ +I"-

JMeter安装

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/c7/793940fc42a0badec0de24c0eb990dc1af6d6beb8628878acbf6c2229d1961 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/c7/793940fc42a0badec0de24c0eb990dc1af6d6beb8628878acbf6c2229d1961 new file mode 100644 index 0000000000..80aa84e7fd --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/c7/793940fc42a0badec0de24c0eb990dc1af6d6beb8628878acbf6c2229d1961 @@ -0,0 +1,144 @@ +I"

字符串转换(每个类都需要__repr__)

+ +

在 Python 中定义一个自定义类之后,如果尝试在控制台中输出其实例或在解释器会话中查看,并不能得到十分令人满意的结果。默认的“转换成字符串”功能非常原始,缺少细节:

+ +
class Person:
+    def __init__(self, name, age):
+        self.name = name
+        self.age = age
+Person_Bob = Person('Bob', 35)
+print(Person_Bob)
+
+ +

输出结果:

+ +
<__main__.Person object at 0x0000015BC9246640>
+
+ +

可以看到,print只显示类名和对象实例id的字符串(这是CPython中对象的内存地址)。

+ +

如何查看具体的内容?

+ +
    +
  1. 直接打印类的属性
  2. +
  3. 向类中加入自定义的to_string()方法来绕过这个问题。
  4. +
+ +
class Person:
+    def __init__(self, name, age):
+        self.name = name
+        self.age = age
+    def to_string(self):
+        return f'Person(name={self.name}, age={self.age})'
+Person_Bob = Person('Bob', 35)
+print(Person_Bob.to_string())
+
+ +

输出结果:

+ +
Person(name=Bob, age=35)
+
+ +

但是,这样做有一个问题,如果我们想要打印一个列表中的所有对象,那么就需要一个一个地调用to_string()方法。这样做显然不够优雅。

+ +

或者,直接打印类的属性,还是不够优雅:

+ +
class Person:
+    def __init__(self, name, age):
+        self.name = name
+        self.age = age
+Person_Bob = Person('Bob', 35)
+print(Person_Bob.name)
+print(Person_Bob.age)
+
+ +

可以向类中添加双下划线方__str__和__repr__。这两个方法以具有 Python 特色的方式在不同情况下将对象转换为字符串。

+ +
class Person:
+    def __init__(self, name, age):
+        self.name = name
+        self.age = age
+    def __str__(self):
+        return f'Person(name={self.name}, age={self.age})'
+
+Person_Bob = Person('Bob', 35)
+print(Person_Bob)
+Person_Bob
+
+
+ +

输出结果:

+ +
Person(name=Bob, age=35)
+Out[6]: <__main__.Person at 0x15bc9249250>
+
+ +

可以看到,在控制台中查看 Car 对象得到的依然是之前包含对象 id 的结果,但是打印对象就会得到由新添加的__str__方法返回的字符串。

+ +

现在,我们可以格式化字符串,而不必担心对象的内部细节:

+ +
f'{Person_Bob}'
+'{0}'.format(Person_Bob)
+
+ +

__repr__和__str__的区别

+ +

__repr__和__str__的区别在于,__str__是面向用户的,而__repr__是面向开发者的。__repr__的目的是为了让开发者能够更容易地理解对象的内容,而__str__的目的是为了让用户能够更容易地理解对象的内容。

+ +

我们在类中定义了__str__方法,在解释器会话中查看Person_Bob对象时,返回结果依然是<main.Person at 0x15bc9249250>。这是因为解释器会话中的对象是__repr__方法返回的字符串,而不是__str__方法返回的字符串。

+ +
class Car:
+    def __init__(self,color,mileage):
+        self.color = color
+        self.mileage = mileage
+    def __str__(self):
+        return f'a {self.color} car'
+    def __repr__(self):
+        return f'Car(color={self.color}, mileage={self.mileage})'
+my_car = Car('red', 37281)
+my_car # 在解释器会话中查看对象,返回的是__repr__方法的结果
+# Out[8]: Car(color=red, mileage=37281)
+print(my_car) # 在控制台中查看对象,返回的是__str__方法的结果
+# a red car
+'{0}'.format(my_car) # 使用format函数,返回的是__str__方法的结果
+# a red car
+
+ +

从中可以看出,在 Python 解释器会话中查看对象得到的是对象的__repr__结果。

+ +

有趣的是,像列表和字典这样的容器总是使用__repr__的结果来表示所包含的对象,哪怕对容器本身调用 str()也是如此。

+ +

为了手动选择其中一种字符串转换方法,最好使用内置的str()和 repr()函数。

+ +

在 date 对象上调用 str()时,得到的是一些看起来像 ISO 日期格 +式的东西;__repr__侧重的则是得到无歧义的结果,此在对象上调用 repr()会得到相对更复杂的结 +果,其中甚至包括完整的模块和类名称:

+ +
import datetime
+today = datetime.date.today()
+# Out[22]: '2023-02-18'
+str(today)
+# Out[23]: 'datetime.date(2023, 2, 18)'
+repr(today)
+
+ +

复制并粘贴由这个__repr__返回的字符串,可以作为有效的 Python 语句重新创建原 date对象。

+ +

为什么每个类都需要__repr__

+ +

如果没有定义__repr__方法,那么Python会使用默认的__repr__方法,这个方法返回的是对象的内存地址,这对于调试来说是非常不方便的。因此建议总是为自定义类添加__repr__方法!!!

+ +
class Car:
+    def __init__(self,color,mileage):
+        self.color = color
+        self.mileage = mileage
+    def __repr__(self):
+        return f'Car({self.color!r}, {self.mileage!r})'
+
+ +

这里使用!r 转换标志来确保输出字符串使用的是 repr(self.color)和 repr(self. mileage),而不是 str(self.color)和 str(self.mileage)。

+ +

来源

+ +

《深入理解Python特性》P051 - P058

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/cb/47a38153dbf0934c2604e36638334450016fe85f37ca906e29a426f23d2b0e b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/cb/47a38153dbf0934c2604e36638334450016fe85f37ca906e29a426f23d2b0e new file mode 100644 index 0000000000..880c1fc996 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/cb/47a38153dbf0934c2604e36638334450016fe85f37ca906e29a426f23d2b0e @@ -0,0 +1,6 @@ +I"a + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/cd/4a6925972d2020313cc89e1f9e0291a6cd69a39212d026c51c42757fe97959 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/cd/4a6925972d2020313cc89e1f9e0291a6cd69a39212d026c51c42757fe97959 new file mode 100644 index 0000000000..f34ddb5998 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/cd/4a6925972d2020313cc89e1f9e0291a6cd69a39212d026c51c42757fe97959 @@ -0,0 +1,207 @@ +I"

数据来源

+ +

数据来源于和鲸社区-2022年中国狭义乘用车分车型销量数据

+ +

查看数据

+ +
import pandas as pd
+df = pd.read_csv('car_sales_2022.csv', names=['rank', 'model', 'manufacturer', 'category', 'segment', 'is_new_energy', 'is_pure_electric', 'price', 'price_min', 'price_max', 'price_mean', 'sales'], header=0)
+print(df.head(10))
+import matplotlib.pyplot as plt
+plt.rcParams['font.sans-serif'] = ['SimHei']
+plt.rcParams['axes.unicode_minus'] = False
+
+ +

结果如下:

+ +

20230404085334

+ +

数据清理

+ +
print("数据清理前行数:", len(df))
+df.drop(df[(df['price_mean'] ==0) | (df['price_mean'].isna())].index, inplace=True)
+print("数据清理后行数:", len(df))
+
+ +

结果如下:

+ +
数据清理前行数: 612
+数据清理后行数: 607
+
+ +

数据查看

+ +

按品牌统计车的数量

+ +
print(df.groupby('manufacturer')['model'].count().sort_values(ascending=False))
+
+ +

结果如下:

+ +
manufacturer
+长安汽车     24
+长城汽车     23
+比亚迪      20
+吉利汽车     20
+上汽集团     17
+         ..
+云度新能源     1
+云雀汽车      1
+华晨鑫源      1
+创维汽车      1
+智己汽车      1
+Name: model, Length: 106, dtype: int64
+
+ +

查看汽车类型

+ +
# 查看category的取值
+print(df['category'].unique())
+# 画柱状图,显示category在不同价格区间的车的数量
+plt.figure(figsize=(10, 6))
+plt.bar(df['category'].unique(), df.groupby('category')['model'].count())
+# ['model'] 为列名,['category'] 为行名
+plt.title('不同车型的数量')
+
+ +

结果如下:

+ +

20230404085930

+ +
#以category为行名,model为列名,显示销量
+pd.pivot_table(df, values='sales', index='category', columns='model')
+
+ +

结果如下:

+ +

20230404090144

+ +
# 查看segment的取值
+print(df['segment'].unique())
+# 画饼状图,显示segment的占比
+plt.figure(figsize=(6, 6))
+plt.pie(df.groupby('segment')['model'].count(), labels=df['segment'].unique(), autopct='%1.1f%%')
+plt.title('车型的车身类型占比')
+
+ +

结果如下:

+ +

20230404090252

+ +

查看新能源车的数量

+ +
# 查看新能源车和非新能源车的销售量
+print(df.groupby('is_new_energy')['sales'].sum())
+
+# 查看新能源车和非新能源车的销售量占比
+print(df.groupby('is_new_energy')['sales'].sum() / df['sales'].sum())
+
+# 画图
+
+plt.figure(figsize=(6, 6))
+plt.pie(df.groupby('is_new_energy')['sales'].sum(), labels=['非新能源车', '新能源车'], autopct='%1.1f%%')
+plt.title('新能源车和非新能源车的销售量占比')
+plt.show()
+
+ +

结果如下:

+ +
is_new_energy
+False    14619974
+True      5512203
+Name: sales, dtype: int64
+is_new_energy
+False    0.726199
+True     0.273801
+Name: sales, dtype: float64
+
+ +

20230404090401

+ +

按价格区间统计车的品牌和数量

+ +
# 按价格区间统计车的品牌和数量
+bins = [5, 10, 15, 20, 30, 50, 100]
+labels = ['5-10', '10-15', '15-20', '20-30', '30-50', '50-100']
+df['price_range'] = pd.cut(df['price_mean'], bins=bins, labels=labels)
+print(df.groupby('price_range')['model'].value_counts())
+
+plt.figure(figsize=(10,6))
+top_30 = df.nlargest(30, 'sales') # 取销量前30的车型
+plt.scatter(top_30['price_mean'], top_30['sales'], s=100, alpha=0.5, c=top_30['sales'], cmap='Reds')
+plt.colorbar(label='销量(台)')
+
+for i, txt in enumerate(top_30['model']):
+    #plt.annotate(txt, (top_30['price_mean'].iloc[i], top_30['sales'].iloc[i])) 
+    plt.annotate(txt, (top_30['price_mean'].iloc[i], top_30['sales'].iloc[i]), xytext=(5, 5), textcoords='offset points', fontsize=8, ha='center') 
+    # 标注车型 enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,
+    # 同时列出数据和数据下标,一般用在 for 循环当中。
+    # iloc[] 通过行号来获取行数据
+
+plt.xlabel('平均价格')
+plt.ylabel('销量')
+plt.title('销量前30的车型价格与销量关系散点图')
+plt.show()
+
+ +

结果如下:

+ +
price_range  model   
+5-10         VGV U70       1
+             VGV U70Pro    1
+             YARiS L 致享    1
+             YARiS L 致炫    1
+             YOUNG光小新      1
+                          ..
+50-100       红旗H9          1
+             英菲尼迪QX60      1
+             蔚来ES7         1
+             蔚来ES8         1
+             飞行家           1
+Name: model, Length: 597, dtype: int64
+
+ +

20230404090646

+ +

查看比亚迪销售情况

+ +
print(df[df['manufacturer']=='比亚迪'][['model', 'price_mean', 'sales']])
+plt.figure(figsize=(10,6))
+df_byd = df[df['manufacturer']=='比亚迪']
+plt.scatter(df_byd['price_mean'], df_byd['sales'], s=100, alpha=0.5, c='blue')
+for i, txt in enumerate(df_byd['model']):
+    plt.annotate(txt, (df_byd['price_mean'].iloc[i], df_byd['sales'].iloc[i])) 
+
+plt.xlabel('价格')
+plt.ylabel('销量')
+plt.title('比亚迪车型价格与销量关系散点图')
+plt.show()
+
+ +

结果如下:

+ +
          model  price_mean   sales
+2    宋PLUS DM-i       18.68  388048
+13           海豚       12.68  204226
+15   秦PLUS DM-i       13.98  188522
+16        元PLUS       15.38  167220
+28          汉EV       27.58  143895
+35          汉DM       26.98  128524
+39          唐DM       27.08  125368
+47     秦PLUS EV       16.13  119755
+96     宋PLUS EV       19.53   66935
+102      驱逐舰 05       13.98   61949
+121          海豹       25.13   51200
+160         秦EV       14.98   33665
+181        元Pro       10.88   26313
+199         唐EV       31.28   23217
+226   宋Pro DM-i       15.33   20323
+242       比亚迪e2       12.33   17790
+308   宋MAX DM-i       16.13   11037
+385       比亚迪e3       15.48    5726
+488      护卫舰 07       24.63    1805
+529       比亚迪D1       16.28     994
+
+ +

20230404090910

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/cf/b655bc3e0f254694d8d1624f4a07a3b19ed39c71c775c6e87d3b40a16112fd b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/cf/b655bc3e0f254694d8d1624f4a07a3b19ed39c71c775c6e87d3b40a16112fd new file mode 100644 index 0000000000..b62092fa85 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/cf/b655bc3e0f254694d8d1624f4a07a3b19ed39c71c775c6e87d3b40a16112fd @@ -0,0 +1,9 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d0/b40f77b9b019d6a3fa8ffd054d26156484d8e6c0effa6472dd6cce10d7d7dc b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d0/b40f77b9b019d6a3fa8ffd054d26156484d8e6c0effa6472dd6cce10d7d7dc new file mode 100644 index 0000000000..f435041429 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d0/b40f77b9b019d6a3fa8ffd054d26156484d8e6c0effa6472dd6cce10d7d7dc @@ -0,0 +1,2 @@ +I"!

断言

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d2/bdac414aca4c0d9ad46165bfd14a98b97d3e8c590475acbbf9dab978c21a34 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d2/bdac414aca4c0d9ad46165bfd14a98b97d3e8c590475acbbf9dab978c21a34 new file mode 100644 index 0000000000..dea0544177 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d2/bdac414aca4c0d9ad46165bfd14a98b97d3e8c590475acbbf9dab978c21a34 @@ -0,0 +1,2 @@ +I"!

主要包括pytest安装;pytest创建简单的测试用例;pytest基础使用如函数级别的方法和类级别的方法;pytest常见插件如pytest-html将测试结果以html的形式展示出来,pytest-rerunfailures可以进行测试代码的失败重试;pytest的数据参数化,可以实习同样的测试用例函数对多个输入的测试数据的测试;最后是根据pytest运行原则以及pytest.ini指定测试的文件夹,测试函数等对之前的接口模块函数进行重构,进行自动化测试。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d3/27c1432636b803b627fa04cba38ed5367ebc27b624654f5cd26a359c6c5b88 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d3/27c1432636b803b627fa04cba38ed5367ebc27b624654f5cd26a359c6c5b88 new file mode 100644 index 0000000000..c0c51a90bc --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d3/27c1432636b803b627fa04cba38ed5367ebc27b624654f5cd26a359c6c5b88 @@ -0,0 +1,5 @@ +I"

1.安装Pycharm

+

Pycharm现在也以snap软件包的形式提供,可以使用命令行的方式进行安装

+
sudo snap install pycharm-community --classic
+
+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d3/3249943bd12c579125c3d88e0f730f0ce601533d3eba71db4240acf0398842 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d3/3249943bd12c579125c3d88e0f730f0ce601533d3eba71db4240acf0398842 new file mode 100644 index 0000000000..9c9ce27dbd --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d3/3249943bd12c579125c3d88e0f730f0ce601533d3eba71db4240acf0398842 @@ -0,0 +1,2 @@ +I"H

BDD接口测试是什么?

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d3/c070f1e7fd82a9553f927aa8500d2defaac8c1f6979f594a4e9948b2ce828d b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d3/c070f1e7fd82a9553f927aa8500d2defaac8c1f6979f594a4e9948b2ce828d new file mode 100644 index 0000000000..68e3c74e60 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d3/c070f1e7fd82a9553f927aa8500d2defaac8c1f6979f594a4e9948b2ce828d @@ -0,0 +1,5 @@ +I"m + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d4/b82eeaa55f29477b48ce45e3633583876bf0cbe3f0262b504d1d1206dec006 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d4/b82eeaa55f29477b48ce45e3633583876bf0cbe3f0262b504d1d1206dec006 new file mode 100644 index 0000000000..9e3cdca6da --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d4/b82eeaa55f29477b48ce45e3633583876bf0cbe3f0262b504d1d1206dec006 @@ -0,0 +1,33 @@ +I"=

apt修改为阿里源, sudo vim /etc/apt/sources.list之后出现问题:

+ +
W: 无法下载 http://mirrors.aliyun.com/ubuntu/dists/jammy/InRelease  暂时不能解析域名“mirrors.aliyun.com”
+W: 无法下载 http://mirrors.aliyun.com/ubuntu/dists/jammy-security/InRelease  暂时不能解析域名“mirrors.aliyun.com”
+W: 无法下载 http://mirrors.aliyun.com/ubuntu/dists/jammy-updates/InRelease  暂时不能解析域名“mirrors.aliyun.com”
+W: 无法下载 http://mirrors.aliyun.com/ubuntu/dists/jammy-proposed/InRelease  暂时不能解析域名“mirrors.aliyun.com”
+W: 无法下载 http://mirrors.aliyun.com/ubuntu/dists/jammy-backports/InRelease  暂时不能解析域名“mirrors.aliyun.com”
+W: 无法下载 https://ppa.launchpadcontent.net/deadsnakes/ppa/ubuntu/dists/jammy/InRelease  暂时不能解析域名“ppa.launchpadcontent.net”
+W: 部分索引文件下载失败。如果忽略它们,那将转而使用旧的索引文件。
+
+
+ +

原因

+ +

DNS没有配置好

+ +

解决

+ +
sudo  vim /etc/resolv.conf
+# 或
+sudo vim /run/systemd/resolve/stub-resolv.conf # 建议改这个
+
+ +

添加nameserver,如下:(使用阿里巴巴提供的DNS域名解析服务)

+ +
# 添加
+# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
+#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
+
+nameserver 223.5.5.5
+nameserver 223.6.6.6
+
+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d7/3d3b73aaac1d6cd305474b7db6996735d1a203fef9f6f07db5e4eefad3af8c b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d7/3d3b73aaac1d6cd305474b7db6996735d1a203fef9f6f07db5e4eefad3af8c new file mode 100644 index 0000000000..fa86e42578 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d7/3d3b73aaac1d6cd305474b7db6996735d1a203fef9f6f07db5e4eefad3af8c @@ -0,0 +1,67 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d8/581a711ead1c7e512e795d4b1c2abb9ec7c94beabfb41c1bf61968e7d02a5b b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d8/581a711ead1c7e512e795d4b1c2abb9ec7c94beabfb41c1bf61968e7d02a5b new file mode 100644 index 0000000000..04b99ac693 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d8/581a711ead1c7e512e795d4b1c2abb9ec7c94beabfb41c1bf61968e7d02a5b @@ -0,0 +1,2 @@ +I"D

1. 什么是知识卡片

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d8/d26f5257e81825750fabb07fef0f79fcd466bfe5d09fd8db5a3bb47dcdb588 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d8/d26f5257e81825750fabb07fef0f79fcd466bfe5d09fd8db5a3bb47dcdb588 new file mode 100644 index 0000000000..e7969dd2d1 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d8/d26f5257e81825750fabb07fef0f79fcd466bfe5d09fd8db5a3bb47dcdb588 @@ -0,0 +1,16 @@ +I") + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d9/5b350b89da785ade70ae9e8342915a5b75ef081c5b803eb4d49bf62e86dbc0 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d9/5b350b89da785ade70ae9e8342915a5b75ef081c5b803eb4d49bf62e86dbc0 new file mode 100644 index 0000000000..a46bff45da --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d9/5b350b89da785ade70ae9e8342915a5b75ef081c5b803eb4d49bf62e86dbc0 @@ -0,0 +1,18 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d9/68dabd18d62274a0cde04cdb1012f9bdcfde94c30c85ab03874bd19e88eb90 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d9/68dabd18d62274a0cde04cdb1012f9bdcfde94c30c85ab03874bd19e88eb90 new file mode 100644 index 0000000000..dc1466aced --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d9/68dabd18d62274a0cde04cdb1012f9bdcfde94c30c85ab03874bd19e88eb90 @@ -0,0 +1,2 @@ +I"m

字符串转换(每个类都需要__repr__)

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d9/696f61be8c95089b3162bacba2f98c766aff94766cf17479a108c8a9fff35e b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d9/696f61be8c95089b3162bacba2f98c766aff94766cf17479a108c8a9fff35e new file mode 100644 index 0000000000..ff8c0bd87d --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d9/696f61be8c95089b3162bacba2f98c766aff94766cf17479a108c8a9fff35e @@ -0,0 +1,110 @@ +I"

1.安装Pycharm

+

Pycharm现在也以snap软件包的形式提供,可以使用命令行的方式进行安装

+
sudo snap install pycharm-community --classic
+
+ +

2.安装python

+
sudo apt install python3
+
+

查看python版本

+
python3 --version
+
+

3.安装git

+
sudo apt install git
+
+

查看git版本

+
git --version
+
+

4.配置git环境

+

因为Gitee会用到SSH,因此需要在shell里检查是否可以连接到Gitee.

+
ssh -T git@gitee.com
+
+

如果出现以下内容,说明可以连接,需要进行下一步的配置。

+
Warning: Permanently added ‘***’ (RSA) to the list of known hosts.
+Permission denied (publickey).
+
+

去注册一个Gitee账号,然后配置邮箱。

+ +

接着,安装SSH Keys(这一步一定要在~/.ssh目录下进行,否则会报错)。

+
cd ~/.ssh
+ls
+
+

如果没有id_rsa和id_rsa.pub文件,就需要生成SSH Keys。

+
# 备份并移除已经存在的ssh keys,以防万一
+mkdir backup
+mv id_rsa* backup
+rm id_rsa*
+# 生成ssh keys
+ssh-keygen -t rsa -C "***@***.com"
+
+

运行的时候会出来输入文件名,就输入id_rsa就可以

+ +

接着又会提示你输入两次密码(该密码是你push文件的时候要输入的密码,而不是github管理者的密码),

+ +

当然,你也可以不输入密码,直接按回车。那么push的时候就不需要输入密码,直接提交到gitee上了。

+ +

发现,id_rsa(私钥)和id_rsa.pub(公钥)这两个文件被创建了。

+ +

最后一步,将公钥添加到Gitee上。 +(1)利用gedit/cat命令,查看id_rsa.pub的内容 +(2)在GitHub中,依次点击Settings -> SSH Keys -> Add SSH Key,将id_rsa.pub文件中的字符串复制进去,注意字符串中没有换行和空格。 +(3) 检查SSH Keys是否添加成功

+
ssh -T git@gitee.com
+
+

如果出现以下内容,说明可以连接,需要进行下一步的配置。

+
Hi Chamjeumlam! You’ve successfully authenticated, but GitHub does not provide shell access.
+
+ +

通过以上的设置之后,就能够通过SSH的方式,直接使用Git命令访问Gitee托管服务器了.

+ +

5.配置Pycharm的python

+

在Pycharm中,选择File -> Settings -> Project -> Project Interpreter -> Add -> Existing environment -> 选择python的安装路径 -> OK +python的安装路径可以通过命令行查看

+
which python3
+
+

6.配置Pycharm的git

+

在Pycharm中,选择File -> Settings -> Version Control -> Git -> Path to Git executable -> 选择git的安装路径 -> OK +git的安装路径可以通过命令行查看

+
which git
+
+

7.配置Pycharm的gitee

+

下载安装gitee + 1、在Setting中选择Plugins# + 2、在Marketplace下搜索框中搜索gitee + 3、点击Install进行下载安装 + 4、安装完后点击Restart IDE +登陆码云 + 1、File->Setting->搜索gitee# + 2、点击Add account# + 3、输入账号密码进行登陆 +登陆成功后,会有登陆信息提示.

+

8. 建立远程仓库并提交代码

+

在Pycharm中,选择VCS -> Import into Version Control -> Share Project on Gitee -> 输入Gitee的用户名和密码 -> OK

+ +

做完这一步,提示错误:successfully created project on Gitee but initial commit failed. +需要进行设置,告诉gitee是谁在运行和更改代码。 +Git 全局设置:

+
git config --global user.name "ChanJeunlam"
+git config --global user.email "1120952003@qq.com"
+
+

创建 git 仓库:

+
mkdir InterAutoTest_W
+cd InterAutoTest_W
+git init 
+touch README.md
+git add README.md
+git commit -m "first commit"
+git remote add origin https://gitee.com/chanjeunlam/InterAutoTest_W.git
+git push -u origin "master"
+
+

已有仓库?

+
cd existing_git_repo
+git remote add origin https://gitee.com/chanjeunlam/InterAutoTest_W.git
+git push -u origin "master"
+
+ +

9. Pycharm中其他设置

+

更改字体大小: +1、点击顶部菜单栏中的File选项,接着点击Settings选项;2、依次打开Editor、Font选项;3、根据需要设置字体和字号即可。

+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d9/a38deb6a312229ee32029fde457ecf21977f11be1f3369faf9b28075a3bfa0 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d9/a38deb6a312229ee32029fde457ecf21977f11be1f3369faf9b28075a3bfa0 new file mode 100644 index 0000000000..ef7f1ab352 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/d9/a38deb6a312229ee32029fde457ecf21977f11be1f3369faf9b28075a3bfa0 @@ -0,0 +1,2 @@ +I"-

单元测试

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/db/0e758dca563da5b4ab6282cd0db7818eada9628e4f677af65cf3ede448a74a b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/db/0e758dca563da5b4ab6282cd0db7818eada9628e4f677af65cf3ede448a74a new file mode 100644 index 0000000000..ea37aea613 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/db/0e758dca563da5b4ab6282cd0db7818eada9628e4f677af65cf3ede448a74a @@ -0,0 +1,191 @@ +I"$

介绍python中引用与对象,可变数据类型与不可变数据类型,值传递,变量和内存管理等方面的内容

+ +

引用与对象

+ +

Python 中,一切皆对象。 每个对象由:标识(identity)、类型(type)、value(值) +组成。

+
    +
  1. 标识用于唯一标识对象,通常对应于对象在计算机内存中的地址。使用内置函数 id(obj)可返回对象 obj 的标识。
  2. +
  3. 类型用于表示对象存储的“数据”的类型。类型可以限制对象的取值范围以及可执行的 +操作。可以使用 type(obj)获得对象的所属类型。
  4. +
  5. 值表示对象所存储的数据的信息。使用 print(obj)可以直接打印出值。
  6. +
+ +

对象的本质就是:一个内存块,拥有特定的值,支持特定类型的相关操作.

+ +

变量也成为:对象的引用。因为,变量存储的就是对象的地址。 +变量通过地址引用了“对象”。

+ +

简单来说,在 Python中,采用的是基于值的内存管理方式,每个值在内存中只有一份存储。如果给多个变量赋相同的值(如数值类型、字符串类型、元组),那么多个变量存储的都是指向这个值的内存地址,即id。也就是说,Python中的变量并没有存储变量的值,而是存储指向这个值的地址或引用。

+ +

引用与对象

+ +

Python 的赋值机制

+ +
x = 666
+y = x
+print(id(x))
+print(id(y))
+
+x = x + 1
+print(id(x))
+print(id(y))
+
+

结果如下:

+
2015992431536
+2015992431536
+2015992431024
+2015992431536
+
+

执行以上三句,在 python 中意味着什么, 实际发生了什么?

+ +

x = 666会在内存中创建一个整型对象,然后变量x相当于一个标签,贴在此对象上。 +y = x 将y也作为这个整型对象的标签,而不是重新在内存中创建一个对象。 +x = x+1 将 x 的值加 1,然后在内存中创建另一个整型对象667,将x贴在这个对象上,而不是原来的666 上。

+ +
a=3
+b=3
+c=1
+print("a is b结果:"+str(a is b))  #True
+print("a is c结果:"+str(a is c))  #False
+print("id(a)=%d,id(b)=%d,id(c)=%d"%(id(a),id(b),id(c)))
+
+

运行结果如下:

+
a is b结果:True
+a is c结果:False
+id(a)=140730432239456,id(b)=140730432239456,id(c)=140730432239392
+
+

对象保存在内存空间,这里的’a’和’b’是变量,变量是对象的引用,外部想要使用对象的值,需要通过变量来引用对象。同样的值在内存中只有一份存储,所以’a’和’b’引用的对象是相同的,id(a)=140730432239456=id(b),这个对象的引用数量为2。当某个对象的引用数量为0时,对象会被回收。

+ +

变量回收机制

+

当我们使用 del 删除变量时,我们到底做了什么? +一句话 : 删除贴在对象上的标签,而不是真的删除内存中的对象。 +那么我们就无法删除内存中的对象,然后节省内存吗? +可以,但是需要通过 python 的垃圾回收机制。简单来说,就是贴在对象上的标签数量为 0 时,会被 python 自动回收。

+ +

可变数据类型与不可变数据类型

+ + + +

这里的可变不可变,是指内存中的那块内容(value)是否可以被改变。如果是不可变类型,在对对象本身操作的时候,必须在内存中新申请一块区域(因为老区域不可变)。如果是可变类型,对对象操作的时候,不需要再在其他地方申请内存,只需要在此对象后面连续申请(+/-)即可,也就是它的address会保持不变,但区域会变长或者变短。

+ +

也就是说:

+ + +

可变对象

+ +

不可变对象

+ +

引用传递与值传递

+ +

可变对象为引用传递(传址),不可变对象为值传递(传值)。

+ +

传值和传址的区别 +传值就是传入一个参数的值,传址就是传入一个参数的地址,也就是内存的地址(相当于指针)。他们的区别是如果函数里面对传入的参数重新赋值,函数外的全局变量是否相应改变,用传值传入的参数是不会改变的,用传址传入就会改变

+ +

Python函数参数传递方式:传递对象引用(传值和传址的混合方式),如果是数字,字符串,元组(不可变对象)则传值;如果是列表,字典(可变对象)则传址;

+ +

举个例子:

+
a=1
+def f(a):
+    a+=1
+f(a)
+print(a)
+
+ +

结果如下:

+ +
1
+
+ +

这段代码里面,因为a是数字类型,所以是传值的方式,a的值并不会变,输出为1

+ +
a=[1]
+def f(a):
+    a[0]+=1
+f(a)
+print(a)
+
+ +

结果如下:

+ +
[2]
+
+ +

这段代码里面,因为a的类型是列表,所以是传址的形式,$a[0]$的值会改变,输出为$[2]$.

+ +

浅拷贝和深拷贝

+ +

不止是函数里面,函数外面的引用也同样遵循这个规则:

+ +
a=1
+b=a
+a=2
+print(a,b)
+a=[1]
+b=a
+a[0]=2
+print(a,b)
+
+

输出结果如下:

+
2 1
+[2] [2]
+
+

所以在python中,当运行上面的代码时,如果a是字典或者列表的话,程序执行的操作并不是新建一个b变量,然后a的值复制给b,而是新建一个b变量,把b的值指向a,也就是相当于在c语言里面的新建一个指向a的指针。 +所以当a的值发生改变时,b的值会相应改变。

+ +

如果想要a的值改变时,b的值不改变,就需要用到浅拷贝和深拷贝。

+ +
import copy
+
+a=[1,2,3]
+b=a
+a.append(4)
+print(a,b)
+
+a=[1,2,3]
+b=copy.copy(a)
+a.append(4)
+print(a,b)
+
+

输出结果如下:

+
[1, 2, 3, 4] [1, 2, 3, 4]
+[1, 2, 3, 4] [1, 2, 3]
+
+

这里用了copy来让b与a相等,后面如果修改了a的值,b的值并不会改变。看来copy已经可以实现我们上面的提到的需求了,那么deepcopy又有什么用?

+ +

如果我们遇到这种情况,copy就解决不了了:

+
import copy
+a = [1,[1,2],3]
+b = copy.copy(a)
+a[1].append(3)
+print(a,b)
+
+

输出结果如下:

+
[1, [1, 2, 3], 3] [1, [1, 2, 3], 3]
+
+

这里a和b的值都改变了,这是因为copy只是复制了一层,如果a里面还有列表,那么b里面的列表也是指向a里面的列表的,所以a里面的列表改变了,b里面的列表也会改变。 +这时候就需要用到deepcopy了:

+
import copy
+a = [1,[1,2],3]
+b = copy.deepcopy(a)
+a[1].append(3)
+print(a,b)
+
+

输出结果如下:

+
[1, [1, 2, 3], 3] [1, [1, 2], 3]
+
+

这里a的值改变了,b的值没有改变,这是因为deepcopy是递归的复制了所有的值,所以a里面的列表改变了,b里面的列表不会改变。

+ +

总结如下: +浅拷贝:浅拷贝中的元素,是对原对象中子对象的引用。此时如果原对象中某一子对象是可变的,改变后会影响拷贝后的对象,存在副作用。一不小心就会触发很大的问题。

+ +

深拷贝:深拷贝则会递归的拷贝原对象中的每一个子对象,拷贝之后的对象与原对象没有关系。

+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/db/1c22ad0a7ce25a1e040d1df1b5e88604f1b5bfc3b41b043081568648ea382e b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/db/1c22ad0a7ce25a1e040d1df1b5e88604f1b5bfc3b41b043081568648ea382e new file mode 100644 index 0000000000..7fb10bee36 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/db/1c22ad0a7ce25a1e040d1df1b5e88604f1b5bfc3b41b043081568648ea382e @@ -0,0 +1,683 @@ +I"

wind turbine数据,如何使用机器学习算法预测轴承健康程度和风功率曲线呢

+ +

可以使用机器学习算法进行预测。以下是一个基本的流程,希望能为您提供一些帮助:

+ +
    +
  1. 数据收集和准备:收集风力涡轮机的传感器数据,包括机械振动、电流、温度等。确保数据集中包含健康和故障状态下的数据,并对数据进行清理和处理,例如去除缺失值和异常值。
  2. +
  3. 特征工程:从原始数据中提取有用的特征,例如频率和幅度特征。可以使用信号处理技术、统计学方法或深度学习方法进行特征提取。
  4. +
  5. 模型选择和训练:选择适当的机器学习算法进行轴承健康程度和风功率预测,例如支持向量机(SVM)、随机森林(Random Forest)、神经网络等。使用训练数据对模型进行训练,并使用交叉验证技术评估模型性能。
  6. +
  7. 模型优化和部署:通过调整模型参数和优化特征工程过程,进一步提高模型性能。使用测试数据集评估模型的性能,并将其部署到实际应用中。
  8. +
+ +

需要注意的是,风能发电量预测的准确性受多种因素影响,包括风速、风向、空气密度、温度等。因此,最好使用多变量模型进行预测,并考虑使用其他数据源(如天气数据)来提高预测准确性。

+ +

第一步需要收集什么样的数据,怎么进行数据预处理呢

+ +

在收集风力涡轮机数据时,需要收集与轴承健康状态和风能发电量相关的传感器数据,例如振动、电流、温度等。这些传感器数据可以通过直接连接到风力涡轮机上的传感器进行收集,也可以通过数据日志记录器进行收集。

+ +

对于轴承健康状态的预测,需要收集不同状态下的传感器数据,例如正常运行时和轴承故障时的振动数据。收集的数据应该包含不同的轴承类型、负载条件和环境条件。

+ +

对于风能发电量的预测,需要收集与风能发电量相关的传感器数据,例如风速、风向、温度、气压等。这些数据可以来自风场的气象站,也可以通过安装在风力涡轮机上的传感器进行收集。

+ +

在进行数据预处理时,需要进行以下步骤:

+ +
    +
  1. 数据清洗:去除缺失值、异常值、重复值等。
  2. +
  3. 数据转换:将数据转换为适合机器学习算法处理的形式,例如将时间序列数据转换为频域数据。
  4. +
  5. 特征提取:从原始数据中提取有用的特征,例如频率和幅度特征。
  6. +
  7. 特征缩放:将特征缩放到相同的范围内,以便机器学习算法更好地处理数据。
  8. +
  9. 特征选择:选择与轴承健康状态和风能发电量预测相关的特征。
  10. +
  11. 数据划分:将数据集划分为训练集、验证集和测试集。
  12. +
+ +

以上步骤可以提高机器学习算法的性能,并帮助提高预测准确性。

+ +

第一步数据清理python代码

+ +

以下是一些Python代码示例,用于数据清理:

+ +
    +
  1. 去除缺失值
  2. +
+ +

+import pandas as pd
+
+
+# 读取数据集
+
+df = pd.read_csv('data.csv')
+
+
+# 去除缺失值
+
+df.dropna(inplace=True)
+
+
+ +
    +
  1. 去除异常值
  2. +
+ +

+import numpy as np
+
+import pandas as pd
+
+
+# 读取数据集
+
+df = pd.read_csv('data.csv')
+
+
+# 计算异常值的阈值
+
+mean = np.mean(df['column_name'])
+
+std = np.std(df['column_name'])
+
+threshold = 3 * std
+
+
+# 去除异常值
+
+df = df[np.abs(df['column_name'] - mean) < threshold]
+
+
+ +
    +
  1. 去除重复值
  2. +
+ +

+import pandas as pd
+
+
+# 读取数据集
+
+df = pd.read_csv('data.csv')
+
+
+# 去除重复值
+
+df.drop_duplicates(inplace=True)
+
+
+ +

这些代码示例仅仅是对数据清洗的基本操作,实际情况需要根据数据集的具体情况进行适当的修改。

+ +

重复的取值需要去掉吗

+ +

一般来说,重复的取值需要去掉。重复的取值可能会对数据分析和建模产生影响,因此在进行数据清洗时,通常会去除重复的取值。

+ +

在Python中,可以使用Pandas库的drop_duplicates()方法来去除DataFrame中的重复行。例如,假设我们有一个名为df的DataFrame,其中包含重复的行,可以按如下方式去除重复的行:

+ +

+import pandas as pd
+
+
+# 读取数据集
+
+df = pd.read_csv('data.csv')
+
+
+# 去除重复行
+
+df.drop_duplicates(inplace=True)
+
+
+ +

需要注意的是,drop_duplicates()方法默认根据所有列的值来判断是否为重复行,如果只想根据特定列的值来判断是否为重复行,可以在方法中指定subset参数,例如:

+ +

+# 根据 'column_name' 列的值来去除重复行
+
+df.drop_duplicates(subset=['column_name'], inplace=True)
+
+
+ +

另外,有些情况下,重复的取值可能是由于数据采集或存储过程产生的,如果确定这些重复的取值不会对数据分析和建模产生影响,可以保留这些重复的取值。

+ +

请问不同的数据之间需要归一化吗,怎么确定在建模过程中不同数据的权重呢

+ +

在建模过程中,不同的数据通常需要进行归一化。归一化的目的是将不同数据之间的尺度统一,避免不同数据之间的尺度差异对建模结果产生影响。例如,在使用基于距离的算法(如KNN)时,如果不对数据进行归一化,那么具有较大尺度的数据特征将会对距离计算产生更大的影响,从而可能会导致错误的分类结果。

+ +

常用的归一化方法包括最小-最大归一化和Z-score归一化。最小-最大归一化将数据缩放到[0,1]的范围内,而Z-score归一化将数据转换为均值为0,标准差为1的分布。在使用这些方法进行归一化时,需要注意对训练集和测试集进行相同的归一化处理,以避免在测试集上产生不合理的结果。

+ +

在建模过程中,不同数据的权重通常是通过特征选择、特征提取或特征工程等方法来确定的。这些方法可以根据不同的场景和问题,选择合适的特征,从而确定不同数据的权重。在选择特征时,需要考虑特征的相关性、重要性和稳定性等因素,并尽可能地保留有用的信息,以提高建模的准确性和泛化性能。

+ +

我对这句话理解不够深入“不同数据的权重通常是通过特征选择、特征提取或特征工程等方法来确定的”,请问有具体例子吗

+ +

当我们在构建机器学习模型时,我们需要选择哪些数据特征用于训练模型。在这个过程中,我们需要考虑每个特征对模型的贡献以及它们之间的相关性。

+ +

特征选择、特征提取和特征工程是一些常用的方法,用于确定不同数据的权重以及如何将它们组合在一起。以下是一些具体的例子:

+ +
    +
  1. 特征选择
  2. +
+ +

特征选择是一种通过选择最具有预测能力的特征来减少特征数量的技术。这有助于提高模型的准确性并减少过拟合。一些常用的特征选择方法包括卡方检验、皮尔逊相关系数、互信息等。这些方法可以帮助确定哪些特征对于预测目标变量是有用的,从而决定不同数据的权重。

+ +
    +
  1. 特征提取
  2. +
+ +

特征提取是一种将原始数据转换为更具有预测效力的特征的技术。例如,在图像识别问题中,我们可以使用卷积神经网络(CNN)来提取图像的特征,然后将这些特征用于训练分类器。特征提取可以帮助我们确定哪些特征对于预测目标变量是最有用的,并从原始数据中提取出这些特征,从而确定不同数据的权重。

+ +
    +
  1. 特征工程
  2. +
+ +

特征工程是一种将原始数据转换为更有意义的特征的技术。例如,在自然语言处理中,我们可以使用词袋模型来将文本转换为向量表示,并使用TF-IDF等方法来加权不同的单词。在这种情况下,我们可以将不同单词的权重作为不同数据的权重。特征工程可以帮助我们确定哪些特征对于预测目标变量是最有用的,并将这些特征组合在一起以确定不同数据的权重。

+ +

你能否提供一些机器学习模型中常用的特征选择方法?

+ +

当构建机器学习模型时,特征选择是一个重要的步骤。特征选择的目的是从原始数据中选择有用的特征,以提高模型的准确性和泛化性能。以下是一些常用的特征选择方法:

+ +
    +
  1. 方差选择法
  2. +
+ +

方差选择法是一种过滤方法,它基于特征的方差来选择特征。如果一个特征的方差很小,那么该特征对目标变量的预测能力可能很低。因此,方差选择法认为这些特征是不重要的,应该被删除。

+ +
    +
  1. 相关系数法
  2. +
+ +

相关系数法是一种过滤方法,它基于特征与目标变量之间的相关性来选择特征。如果一个特征与目标变量之间的相关性很小,那么该特征对目标变量的预测能力可能很低。因此,相关系数法认为这些特征是不重要的,应该被删除。

+ +
    +
  1. 卡方检验
  2. +
+ +

卡方检验是一种过滤方法,它基于特征与目标变量之间的独立性来选择特征。卡方检验的基本思想是,如果一个特征与目标变量之间是独立的,那么它对目标变量的预测能力可能很低。因此,卡方检验认为这些特征是不重要的,应该被删除。

+ +
    +
  1. 互信息
  2. +
+ +

互信息是一种过滤方法,它基于特征与目标变量之间的信息量来选择特征。互信息的基本思想是,如果一个特征与目标变量之间的信息量很大,那么它对目标变量的预测能力可能很高。因此,互信息认为这些特征是重要的,应该被保留。

+ +
    +
  1. Lasso回归
  2. +
+ +

Lasso回归是一种包装方法,它通过对特征进行稀疏化来选择特征。Lasso回归的基本思想是,如果一个特征对目标变量的预测能力很低,那么它在Lasso回归中的系数可能很小,从而被压缩为0。因此,Lasso回归认为这些特征是不重要的,应该被删除。

+ +

以上是一些常用的特征选择方法,当然还有其他的方法,如随机森林、主成分分析等。在实践中,应根据数据集的特点和问题的需求来选择合适的特征选择方法。

+ +

特征工程这一步涉及到的信号处理技术和统计学方法有哪些呢

+ +

特征工程是机器学习模型构建过程中非常重要的一步,它涉及到从原始数据中提取有意义的特征,以提高模型的准确性和泛化性能。在特征工程过程中,信号处理技术和统计学方法都具有重要的作用。以下是一些常用的信号处理技术和统计学方法:

+ +
    +
  1. 时频分析
  2. +
+ +

时频分析是一种将信号在时间和频率上分解的技术。在特征工程中,时频分析可以用来提取信号的频域特征,如功率谱、频谱峰值等。常用的时频分析方法包括傅里叶变换、小波变换等。

+ +
    +
  1. 滤波
  2. +
+ +

滤波是一种将信号的某些频率成分去除或增强的技术。在特征工程中,滤波可以用来去除噪声、减少信号中的干扰等。常用的滤波方法包括低通滤波、高通滤波、带通滤波等。

+ +
    +
  1. 峰值检测
  2. +
+ +

峰值检测是一种从信号中提取峰值或极值的技术。在特征工程中,峰值检测可以用来提取信号的峰值或极值,例如心电图中的QRS波群。常用的峰值检测方法包括阈值法、斜率法、二阶导数法等。

+ +
    +
  1. 统计分析
  2. +
+ +

统计分析是一种从数据中提取统计特征的技术。在特征工程中,统计分析可以用来提取数据的分布、中心趋势、离散程度等信息。常用的统计分析方法包括均值、标准差、方差、偏度、峰度等。

+ +
    +
  1. 主成分分析
  2. +
+ +

主成分分析是一种从高维数据中提取主要特征的技术。在特征工程中,主成分分析可以用来降维和提取主要特征,以减少数据的复杂性和冗余性。常用的主成分分析方法包括奇异值分解、特征值分解等。

+ +

以上是一些常用的信号处理技术和统计学方法,在实际应用中需要结合具体问题和数据特点来选择合适的方法。

+ +

特征工程这一步涉及到的深度学习方法包括哪些,请至少列出20种,并详细说明他们的优劣势和使用情况(尤其是在风机上)

+ +

特征工程是机器学习模型构建中非常重要的一步,涉及到从原始数据中提取有意义的特征。在特征工程中,深度学习方法可以用来自动地学习数据中的特征,并提高模型的准确性和泛化能力。以下是一些常见的深度学习方法,以及它们在风机上的应用情况、优劣势。

+ +
    +
  1. 卷积神经网络(Convolutional Neural NetworkCNN):主要应用于图像和视频处理任务,在风机中可以用于图像数据的分类和检测。优势在于能够自动提取图像的特征,但需要大量的数据和计算资源。
  2. +
  3. 长短时记忆网络(Long Short-Term Memory,LSTM):是一种特殊的RNN结构,能够有效地处理长期依赖性问题,在风机中可以用于时间序列数据的建模和预测。优势在于能够有效地解决梯度消失和梯度爆炸等问题,但需要更多的计算资源。
  4. +
  5. 自编码器(Autoencoder,AE):主要用于特征提取和降维,在风机中可以用于提取信号的特征表示。优势在于能够自动地学习数据的特征表示,但需要大量的数据和计算资源。
  6. +
  7. 生成对抗网络(Generative Adversarial Network,GAN):主要用于生成新的数据,在风机中可以用于生成合成数据,如合成风速、温度等。优势在于能够生成逼真的合成数据,但需要更多的计算资源和优化技巧。
  8. +
  9. 注意力机制(Attention Mechanism):主要用于提高模型的准确性和泛化能力,在风机中可以用于对重要特征的加权。优势在于能够提高模型的准确性和泛化能力,但需要更多的计算资源。
  10. +
  11. 深度置信网络(Deep Belief Network,DBN):主要用于特征提取和分类,在风机中可以用于信号的分类和故障诊断。优势在于能够自动地学习数据的特征表示和分类器,但需要大量的数据和计算资源。
  12. +
  13. 变分自编码器(Variational Autoencoder,VAE):主要用于生成新的数据和特征提取,在风机中可以用于合成数据和信号的降维。优势在于能够自动地学习数据的特征表示和生成器,但需要更多的计算资源。
  14. +
  15. 深度强化学习(Deep Reinforcement Learning,DRL):主要用于处理决策问题,在风机中可以用于优化风机的控制策略。优势在于能够自动地学习最优的控制策略,但需要更多的计算资源和优化技巧。
  16. +
  17. 对抗训练(Adversarial Training):主要用于提高模型的鲁棒性和泛化能力,在风机中可以用于提高模型的抗干扰能力。优势在于能够提高模型的鲁棒性和泛化能力,但需要更多的计算资源和优化技巧。
  18. +
  19. 循环神经网络(Recurrent Neural Network,RNN):是一种专门用于处理序列数据的神经网络结构,在风机中可以用于时间序列数据的建模和预测。优势在于能够考虑时间序列数据的时序关系和长期依赖性,但需要更多的计算资源和调整超参数。对于风机中的数据,RNN可以用于预测风机的状态和剩余寿命,以及识别故障模式和异常情况。例如,可以使用LSTM或GRU等常见的RNN变体来构建预测模型,并对输入数据进行适当的特征工程和预处理。
  20. +
  21. 卷积自编码器(Convolutional Autoencoder,CAE):是一种自编码器,专门用于图像处理,在风机中可以用于图像数据的降噪和特征提取。优势在于能够自动地学习图像的特征表示和降噪,但需要更多的计算资源。
  22. +
  23. 前馈神经网络(Feedforward Neural Network,FNN):是一种最基本的神经网络结构,在风机中可以用于分类和回归任务。优势在于简单易懂,但需要更多的数据和调整超参数。
  24. +
  25. 残差网络(Residual NetworkResNet):是一种特殊的神经网络结构,主要用于解决梯度消失问题,在风机中可以用于信号分类和故障诊断。优势在于能够解决梯度消失问题和提高模型的准确性,但需要更多的计算资源。
  26. +
  27. 双向循环神经网络(Bidirectional Recurrent Neural Network,BiRNN):是一种RNN结构的变体,能够同时考虑过去和未来的信息,在风机中可以用于时间序列数据的建模和预测。优势在于能够同时考虑过去和未来的信息,但需要更多的计算资源。
  28. +
  29. 深度卷积生成对抗网络(Deep Convolutional Generative Adversarial Network,DCGAN):是一种特殊的GAN结构,主要用于图像生成,在风机中可以用于生成合成图像数据。优势在于能够生成高质量的合成图像,但需要更多的计算资源和优化技巧。
  30. +
  31. 长短时注意力网络(Long Short-Term Attention Network,LSTAN):是一种结合了LSTM和注意力机制的神经网络结构,在风机中可以用于时间序列数据的建模和预测。优势在于能够同时考虑时间序列数据和关键特征,但需要更多的计算资源。
  32. +
  33. 神经网络集成(Neural Network Ensemble):是一种将多个神经网络模型组合在一起的方法,在风机中可以用于提高模型的准确性和鲁棒性。优势在于能够综合多个模型的优点,但需要更多的计算资源和数据。
  34. +
  35. 深度强化学习(Deep Reinforcement Learning,DRL):是一种结合了深度学习和强化学习的方法,在风机中可以用于优化控制策略和调节参数。优势在于能够自动学习最优策略和调节参数,但需要更多的计算资源和调整超参数。
  36. +
  37. 变分自编码器(Variational Autoencoder,VAE):是一种自编码器,能够生成新的样本,用于数据增强,在风机中可以用于数据增强和特征提取。优势在于能够生成高质量的合成数据,但需要更多的计算资源和优化技巧。
  38. +
+ +

以上是一些常见的深度学习方法在特征工程中的应用,不同的方法适用于不同的任务和数据。在风机上,常见的任务包括信号分类、故障诊断、预测和控制优化等。针对不同的任务和数据,可以选择不同的方法来进行特征工程和模型训练。

+ +

特征工程这一步涉及的机器学习算法,在风机上的应用以及优缺点

+ +

特征工程是机器学习的重要步骤,它主要是通过对原始数据进行处理和转换,提取有用的信息和特征,以便机器学习算法能够更好地进行模型训练和预测。在风机领域,特征工程通常包括信号处理、统计分析、频谱分析、小波变换、特征选择等方法。以下是一些常见的机器学习算法,在风机上的应用以及优缺点:

+ +
    +
  1. 支持向量机(Support Vector Machine,SVM):SVM是一种常见的监督学习算法,在风机中可以用于分类和回归任务,例如故障诊断和剩余寿命预测。SVM的优点在于泛化性能好、适用于高维数据和非线性数据,但需要适当调整超参数和处理缺失数据。
  2. +
  3. 决策树(Decision Tree):决策树是一种基于树形结构的分类和回归算法,在风机中可以用于故障诊断和异常检测等任务。决策树的优点在于易于理解和解释、能够处理非线性数据和缺失数据,但容易过拟合和不稳定。
  4. +
  5. 随机森林(Random Forest):随机森林是一种基于多个决策树的集成学习算法,在风机中可以用于故障诊断和预测任务。随机森林的优点在于具有较高的准确性和泛化能力、能够处理高维数据和缺失数据,但需要更多的计算资源和时间。
  6. +
  7. 神经网络(Neural Network):神经网络是一种基于生物神经元结构的人工智能算法,在风机中可以用于故障诊断、剩余寿命预测和控制等任务。神经网络的优点在于能够处理复杂的非线性数据和时序数据、具有良好的泛化性能和自适应性,但需要更多的计算资源和训练时间。
  8. +
  9. 梯度提升决策树(Gradient Boosting Decision Tree,GBDT):GBDT是一种基于决策树的集成学习算法,在风机中可以用于故障诊断和预测任务。GBDT的优点在于能够处理高维数据和非线性数据、具有较高的准确性和泛化能力,但需要较多的计算资源和时间。
  10. +
+ +

总之,在风机领域,不同的机器学习算法具有各自的优缺点,需要根据具体的应用场景选择合适的算法和特征工程方法,以达到最佳的预测性能和效果。

+ +

还有一些其他的机器学习算法吗

+ +

是的,还有其他一些常见的机器学习算法,以下是一些例子:

+ +
    +
  1. K近邻算法(K-Nearest Neighbors,KNN):KNN是一种基于实例的非参数算法,在风机领域可用于故障诊断和分类任务。KNN的优点在于简单易用、不需要训练过程,但需要适当的距离度量和选择K值。
  2. +
  3. 贝叶斯分类器(Bayesian Classifier):贝叶斯分类器是一种基于贝叶斯定理的分类算法,可用于风机中的故障诊断和分类任务。贝叶斯分类器的优点在于具有较高的准确性和泛化能力、能够处理高维数据和缺失数据,但需要适当的假设和模型选择。
  4. +
  5. 主成分分析(Principal Component Analysis,PCA):PCA是一种常见的降维算法,可用于风机中的特征提取和可视化。PCA的优点在于能够提取数据的主要特征和结构、降低模型复杂度和计算成本,但可能会损失一些信息和解释性。
  6. +
  7. 独立成分分析(Independent Component Analysis,ICA):ICA是一种基于统计学的盲源分离算法,可用于风机中的信号分离和特征提取。ICA的优点在于能够提取数据的独立成分和特征、对噪声和干扰具有较好的鲁棒性,但需要适当的假设和调整参数。
  8. +
  9. 聚类分析(Cluster Analysis):聚类分析是一种无监督学习算法,可用于风机中的异常检测和行为分析。聚类分析的优点在于能够发现数据的内在结构和相似性、不需要先验知识和标记,但需要适当的距离度量和聚类方法。
  10. +
  11. 联合概率模型(Joint Probability Model,JPM):JPM是一种基于概率模型的机器学习算法,可用于风机中的异常检测和故障诊断等任务。JPM的优点在于能够建立数据的联合概率分布、对复杂数据具有较好的建模能力,但需要适当的假设和参数估计。
  12. +
  13. 马尔可夫链蒙特卡罗(Markov Chain Monte Carlo,MCMC):MCMC是一种基于马尔可夫链的采样算法,可用于风机中的贝叶斯推断和模型拟合。MCMC的优点在于能够处理高维参数空间和复杂的后验分布、对参数估计具有较好的收敛性和精度,但需要适当的调整步长和采样次数。
  14. +
  15. 集成学习(Ensemble Learning):集成学习是一种基于多个模型的组合算法,可用于风机中的分类、回归和聚类等任务。集成学习的优点在于能够减少模型的方差和偏差、提高模型的准确性和泛化能力,但需要适当的模型选择和集成方式。
  16. +
  17. 深度学习(Deep Learning):深度学习是一种基于多层神经网络的机器学习算法,可用于风机中的故障诊断、剩余寿命预测和控制等任务。深度学习的优点在于能够处理复杂的非线性数据和时序数据、具有良好的泛化性能和表征能力,但需要更多的计算资源和训练时间。
  18. +
+ +

这么多模型,应该怎么选择合适的机器学习算法呢

+ +

选择合适的机器学习算法需要考虑多个因素,包括问题类型、数据特征、计算资源和时间成本等。以下是一些选择机器学习算法的一般性建议:

+ +
    +
  1. 确定问题类型:首先需要确定问题类型,例如分类、回归、聚类、异常检测等。不同的问题类型需要使用不同的算法和评估指标。
  2. +
  3. 分析数据特征:其次需要分析数据的特征,例如数据的维度、分布、缺失值和噪声等。不同的数据特征需要使用不同的特征工程方法和算法。
  4. +
  5. 选择合适的算法:根据问题类型和数据特征,选择合适的机器学习算法。例如,对于分类问题和高维数据,可以考虑使用支持向量机(SVM)或神经网络;对于聚类问题和非线性数据,可以考虑使用K均值聚类或高斯混合模型(GMM)等。
  6. +
  7. 调整参数和超参数:对于选择的算法,需要适当调整参数和超参数,以达到最佳的预测性能和效果。例如,对于SVM算法,需要选择合适的核函数和正则化参数;对于神经网络算法,需要选择合适的层数、节点数和激活函数等。
  8. +
  9. 评估和比较算法:最后需要评估和比较不同的算法和模型,以选择最佳的算法和特征工程方法。常用的评估指标包括准确率、召回率、精度、F1值等。同时,需要注意过拟合和欠拟合问题,避免模型复杂度过高或过低。
  10. +
+ +

总之,在选择机器学习算法时,需要根据具体的问题类型和数据特征,结合实际情况和经验选择合适的算法和特征工程方法,并进行适当调整和优化,以获得最佳的预测性能和效果。

+ +

确定问题类型和分析数据特征看上去很关键。请问你可以列出这两步需要考虑的所有组合,并且为这些组合提供合适的机器学习算法吗

+ +

确定问题类型和分析数据特征确实是选择合适的机器学习算法的关键步骤。以下是一些常见的问题类型和数据特征的组合,以及相应的机器学习算法建议:

+ +
    +
  1. 二分类问题和结构化数据:对于二分类问题和结构化数据,可以考虑使用逻辑回归、决策树、随机森林等算法。
  2. +
  3. 多分类问题和结构化数据:对于多分类问题和结构化数据,可以考虑使用朴素贝叶斯、K近邻、支持向量机(SVM)等算法。
  4. +
  5. 回归问题和结构化数据:对于回归问题和结构化数据,可以考虑使用线性回归、岭回归、Lasso回归等算法。
  6. +
  7. 聚类问题和非结构化数据:对于聚类问题和非结构化数据,可以考虑使用K均值聚类、层次聚类、高斯混合模型(GMM)等算法。
  8. +
  9. 异常检测问题和非结构化数据:对于异常检测问题和非结构化数据,可以考虑使用离群点检测、孤立森林、基于密度的异常检测等算法。
  10. +
  11. 时序预测问题和序列数据:对于时序预测问题和序列数据,可以考虑使用循环神经网络(RNN)、长短时记忆网络(LSTM)、卷积神经网络(CNN)等算法。
  12. +
  13. 图像识别问题和图像数据:对于图像识别问题和图像数据,可以考虑使用卷积神经网络(CNN)、深度残差网络(ResNet)、网络中的网络(NiN)等算法。
  14. +
  15. 自然语言处理问题和文本数据:对于自然语言处理问题和文本数据,可以考虑使用循环神经网络(RNN)、长短时记忆网络(LSTM)、卷积神经网络(CNN)等算法。
  16. +
+ +

需要注意的是,这些组合和算法建议仅供参考,实际选择算法需要结合具体情况进行综合考虑和比较。

+ +

我想了解风机二分类问题,应该怎么下手,最好提供一些机器学习和深度学习的算法案例

+ +

针对风机二分类问题,可以采用以下步骤:

+ +
    +
  1. 数据预处理
  2. +
+ +

首先需要对数据进行预处理,包括数据清洗、数据可视化、数据归一化等,以便更好地应用机器学习算法。特别是对于风机的传感器数据,需要对数据进行去噪、异常值处理、缺失值填充等操作。

+ +
    +
  1. 特征提取
  2. +
+ +

对于风机数据,可以提取多种特征,包括振动信号、温度、湿度等。特别是针对振动信号,可以提取频域特征、时域特征、小波包能量等特征。

+ +
    +
  1. 机器学习算法
  2. +
+ +

针对风机二分类问题,可以采用以下机器学习算法:

+ + + +
    +
  1. 深度学习算法
  2. +
+ +

除了机器学习算法,深度学习算法也可以应用于风机二分类问题中。以下是一些适用于风机二分类问题的深度学习算法:

+ + + +

需要注意的是,机器学习算法和深度学习算法的选择需要结合具体问题进行综合考虑和比较,并且需要进行模型的调参和优化,以提高分类的准确性和效率。同时,对于风机数据的预处理和特征提取也是影响分类效果的重要因素。

+ +

我对随机深林的算法比较感兴趣,请问您接下来我应该如何进行参数调呢

+ +

针对随机森林算法的参数调优,以下是一些常用的方法和技巧:

+ +
    +
  1. n_estimators:这个参数指的是随机森林中决策树的数量。一般来说,n_estimators越大,模型的性能会越好,但同时也会增加模型的计算时间。因此,需要通过交叉验证等方法来确定合适的n_estimators值。
  2. +
  3. max_depth:这个参数指的是决策树的最大深度。一般来说,设置max_depth可以防止过拟合,但同时也会使模型的泛化能力降低。因此,需要通过交叉验证等方法来确定合适的max_depth值。
  4. +
  5. min_samples_split:这个参数指的是分裂一个内部节点需要的最小样本数。一般来说,这个值越小,模型会越容易过拟合。因此,需要通过交叉验证等方法来确定合适的min_samples_split值。
  6. +
  7. min_samples_leaf:这个参数指的是每个叶子节点需要的最小样本数。一般来说,这个值越小,模型会越容易过拟合。因此,需要通过交叉验证等方法来确定合适的min_samples_leaf值。
  8. +
  9. max_features:这个参数指的是在寻找最佳分割点时,随机选择的特征数量。一般来说,max_features越小,模型会越容易过拟合。因此,需要通过交叉验证等方法来确定合适的max_features值。
  10. +
+ +

以上是一些常用的调参方法和技巧,需要注意的是,在调参过程中需要避免过拟合问题,并且需要结合具体问题进行综合考虑和比较。同时,可以采用网格搜索、随机搜索等方法来进行参数搜索和优化。

+ +

通过交叉验证等方法来确定合适的n_estimators值是什么意思呢

+ +

在随机森林算法中,n_estimators是指随机森林中包含的决策树的数量。n_estimators越大,模型的性能通常会更好,但同时也会增加模型的计算时间和存储空间。因此,需要通过交叉验证等方法来确定合适的n_estimators值,以达到平衡模型性能和效率的目的。

+ +

交叉验证是一种常用的机器学习方法,可以将数据集划分为训练集和验证集,并重复进行多次,以评估模型的性能和泛化能力。在确定n_estimators值时,可以采用交叉验证来评估不同的n_estimators值对模型性能的影响。具体来说,可以将数据集划分为训练集和验证集,然后针对不同的n_estimators值训练多个模型,并在验证集上进行评估,得到不同n_estimators值下的模型性能指标。然后,可以比较不同n_estimators值的性能指标,选择最优的n_estimators值作为最终模型的参数。

+ +

需要注意的是,交叉验证的结果具有一定的随机性,因此需要进行多次交叉验证,并取平均值来获得更可靠的结果。同时,为了避免过拟合,还需要在每次交叉验证中随机选择训练集和验证集,并对模型进行平均化处理,以提高模型的泛化能力。

+ +

还不是很懂,看上去参数的选择都需要设计到交叉验证的方法,请问这种方法运行开销是怎么样的呢

+ +

是的,参数选择通常需要采用交叉验证等方法来评估不同参数值对模型性能的影响。交叉验证方法的运行开销取决于数据集的大小和交叉验证的折数等因素。

+ +

交叉验证方法的一般流程是将数据集划分为k个互不重叠的子集,然后选择其中一个子集作为验证集,剩余的k-1个子集作为训练集,进行模型训练和测试,并记录性能指标。然后,循环k次,每次选择不同的验证集,并进行模型训练和测试,最后对性能指标进行平均或求和,得到最终的性能指标。因此,交叉验证的运行开销取决于数据集的大小和交叉验证的折数。通常来说,数据集越大,交叉验证的开销就越大。

+ +

在随机森林算法中,交叉验证通常用于选择合适的参数值,例如n_estimators、max_depth、min_samples_split等。具体来说,可以采用网格搜索或随机搜索等方法,在不同的参数范围内进行搜索,并通过交叉验证方法评估不同参数值对模型性能的影响。搜索过程的开销取决于参数范围的大小和搜索方法的复杂度等因素。

+ +

总的来说,交叉验证方法的运行开销较大,但可以有效评估模型的性能和泛化能力,提高模型的准确性和可靠性。在实际应用中,需要根据具体情况选择合适的交叉验证方法和参数搜索方法,以达到平衡模型性能和效率的目的。

+ +

看上去,参数搜素可以减少运行开销,请问您可以提供具体的参数搜索的方法吗

+ +

是的,参数搜索可以通过精心设计的搜索策略来减少运行开销,并找到最优的参数组合。以下是几种常用的参数搜索方法:

+ +
    +
  1. 网格搜索(Grid Search):网格搜索是一种基于穷举搜索的方法,通过指定参数的范围和步长,对每个参数进行排列组合,然后在所有参数组合中选择性能最好的一组参数。这种方法简单直观,但搜索空间很大时计算开销很大。
  2. +
  3. 随机搜索(Random Search):随机搜索是一种基于随机采样的方法,通过指定参数范围和采样分布,随机采样若干次,然后在所有采样中选择性能最好的一组参数。这种方法计算开销较小,但搜索效率可能较低,因为搜索过程是基于随机采样的。
  4. +
  5. 贝叶斯优化(Bayesian Optimization):贝叶斯优化是一种基于高斯过程的方法,通过建立参数空间的概率模型,不断更新模型的先验概率分布,然后根据模型的预测结果选择下一个参数组合,直到找到性能最好的一组参数。这种方法计算开销较大,但搜索效率较高,在高维参数空间中表现良好。
  6. +
  7. 梯度提升调参(Gradient Boosting Tuning):梯度提升调参是一种基于梯度提升决策树算法的方法,通过不断迭代模型,对每个参数进行微调,然后根据模型的性能指标进行选择,直到找到性能最好的一组参数。这种方法计算开销较大,但搜索效率较高,并且可以避免过度拟合。
  8. +
+ +

需要注意的是,不同的参数搜索方法适用于不同的场景,需要根据具体情况进行选择。同时,为了进一步减少运行开销,可以采用并行计算、分布式计算等方法,加速参数搜索的过程。

+ +

请问如何选择最优的搜索方法?

+ +

选择最优的搜索方法需要考虑多个因素,包括搜索空间大小、搜索效率、搜索准确性、计算资源等。以下是几个选择最优搜索方法的建议:

+ +
    +
  1. 首先需要根据问题的特性和参数搜索空间的大小来选择搜索方法。如果搜索空间较小,可以考虑网格搜索或随机搜索;如果搜索空间较大,可以考虑贝叶斯优化或梯度提升调参等方法。
  2. +
  3. 其次需要考虑搜索效率和搜索准确性的平衡。随机搜索和贝叶斯优化等方法可以在较短时间内找到较好的参数组合,但搜索结果可能并不是全局最优解;而网格搜索和梯度提升调参等方法可以较精确地找到全局最优解,但需要较长的时间。
  4. +
  5. 另外需要考虑计算资源的限制。如果计算资源有限,可以选择随机搜索等方法,通过限制搜索次数或时间来控制计算开销;如果计算资源充足,可以选择网格搜索或贝叶斯优化等方法,以获得更准确的搜索结果。
  6. +
  7. 最后,需要根据具体情况进行实验和评估,选择最优的搜索方法。可以通过交叉验证等方法,评估不同搜索方法的性能和泛化能力,选择最优的搜索方法并得到最优的参数组合。
  8. +
+ +

需要注意的是,选择最优的搜索方法并不是一件容易的事情,需要综合考虑多个因素。同时,搜索方法的选择并不是唯一的,可以根据实际情况进行调整和优化。

+ +

选择了合适的参数之后,一般怎么进行模型训练

+ +

选择了合适的参数之后,可以使用所选参数来训练模型。以下是一般的模型训练流程:

+ +
    +
  1. 数据准备:首先需要准备训练数据和测试数据。训练数据用于模型的训练,测试数据用于模型的验证和评估。可以使用交叉验证等技术来进一步优化数据准备过程。
  2. +
  3. 模型定义:根据所选的算法和参数,定义模型的结构和参数设置。可以使用现有的深度学习框架(如TensorFlow、PyTorch等)来定义模型。
  4. +
  5. 损失函数定义:根据具体问题,选择相应的损失函数(如交叉熵、均方误差等)来描述模型的性能指标。
  6. +
  7. 模型训练:使用训练数据来训练模型。训练过程中,模型根据损失函数的反馈进行参数更新,不断优化模型的性能指标。
  8. +
  9. 模型验证:使用测试数据来验证模型的性能。验证过程中,可以计算模型的准确率、精度、召回率等指标,评估模型的性能和泛化能力。
  10. +
  11. 模型调整:根据验证结果,调整模型的参数设置和结构,优化模型的性能。可以多次进行模型训练和验证,直到获得满意的结果。
  12. +
  13. 模型保存:保存训练好的模型,方便后续的模型预测和应用。
  14. +
+ +

需要注意的是,模型训练过程中需要进行参数初始化、梯度计算、反向传播、优化器选择等操作。同时,为了防止过拟合,需要使用正则化技术、数据增强等方法进行模型调整和优化。

+ +

看上去损失函数很重要,在随机深林的算法中,Ibanez是怎么定义这个损失函数的呢

+ +

Ibanez提出的随机森林算法中,损失函数是构建随机森林模型的一个重要组成部分,它被用来度量模型预测结果与真实标签之间的差距。Ibanez使用的损失函数是分类问题中的交叉熵损失函数,它的定义如下:

+ +\[H(p,q) = - \sum_{i=1}^m p_i \log q_i\] + +

其中,$p_i$表示真实标签的概率分布,$q_i$表示模型预测的概率分布。交叉熵损失函数可以度量模型预测结果与真实标签之间的差距,是分类问题中常用的损失函数之一。当模型的预测结果与真实标签完全一致时,交叉熵损失函数取最小值0;当模型的预测结果与真实标签完全不一致时,交叉熵损失函数取最大值。

+ +

在随机森林算法中,对于每个决策树,都会使用交叉熵损失函数来度量分裂节点的优劣,选择最优的特征和阈值进行分裂。在多棵决策树的集成中,随机森林使用投票的方式来进行分类预测,将每个决策树的预测结果进行投票,得到最终的分类结果。在投票过程中,每个决策树的权重是由交叉熵损失函数来确定的,交叉熵损失函数越小的决策树,权重越大,对最终的分类结果影响越大。

+ +

tell me more

+ +

除了交叉熵损失函数,随机森林算法中还可以使用其他的损失函数来度量模型的性能和预测结果与真实标签之间的差距。常见的损失函数包括:

+ +
    +
  1. 均方误差(Mean Squared Error,MSE):均方误差是回归问题中常用的损失函数,它度量模型预测结果与真实标签之间的平均差距。均方误差越小,模型的预测结果越接近真实标签。在随机森林算法中,可以使用均方误差来选择最优的特征和阈值进行分裂。
  2. +
  3. 绝对误差(Mean Absolute Error,MAE):绝对误差是回归问题中另一种常用的损失函数,它度量模型预测结果与真实标签之间的平均绝对差距。与均方误差不同,绝对误差对误差的大小不敏感,更加稳健。在随机森林算法中,可以使用绝对误差来选择最优的特征和阈值进行分裂。
  4. +
  5. 基尼不纯度(Gini Impurity):基尼不纯度是分类问题中常用的损失函数之一,它度量随机选择一个样本,它被错误分类为其它类别的概率。基尼不纯度越小,表示样本的纯度越高,模型的分类效果越好。在随机森林算法中,可以使用基尼不纯度来选择最优的特征和阈值进行分裂。
  6. +
+ +

随机森林算法中的损失函数选择依赖于具体的问题和数据集,不同的损失函数可能导致不同的模型性能和预测结果。因此,在模型训练过程中,需要根据具体问题和数据集选择合适的损失函数,对模型进行优化和调整。

+ +

模型的性能指标如何选取

+ +

模型的性能指标应该根据具体的问题和任务进行选择。不同的问题和任务可能需要不同的性能指标,因此需要根据具体情况进行选择。

+ +

以下是一些常见的模型性能指标及其适用范围:

+ +
    +
  1. 准确率(Accuracy):准确率是最常用的模型性能指标之一,它度量模型正确分类的样本占总样本数的比例。准确率适用于类别平衡的问题,但对于类别不平衡的问题,准确率可能会忽略掉少数类别,因此需要使用其他指标来衡量模型性能。
  2. +
  3. 精度(Precision):精度是指在所有被预测为正例的样本中,实际为正例的样本占比。精度适用于关注预测结果为正例的问题,如垃圾邮件识别等。
  4. +
  5. 召回率(Recall):召回率是指在所有实际为正例的样本中,被预测为正例的样本占比。召回率适用于关注所有正例被正确预测的问题,如疾病诊断等。
  6. +
  7. F1值(F1 Score):F1值是精度和召回率的调和平均数,它综合考虑了精度和召回率的性能,适用于关注精度和召回率的平衡问题。
  8. +
  9. ROC曲线和AUC值(ROC curve and AUC):ROC曲线展示了模型预测结果的真正率和假正率之间的关系,AUC值是ROC曲线下的面积,表示模型分类效果的综合性能,适用于关注模型分类效果全面性的问题。
  10. +
  11. 平均绝对误差(Mean Absolute Error,MAE):MAE是回归问题中常用的性能指标,它度量模型预测结果与真实标签之间的平均绝对差距,适用于回归问题的性能评估。
  12. +
+ +

在选择模型性能指标时,需要根据具体问题和任务选择合适的指标,并综合考虑模型性能和应用场景的需求,进行权衡和选择.

+ +

在随机深林中,如何构建性能指标,根据损失函数更新参数呢

+ +

在随机深林中,通常使用交叉熵损失函数作为性能指标来度量模型的预测性能。对于每个决策树,都会使用交叉熵损失函数来度量分裂节点的优劣,选择最优的特征和阈值进行分裂。在多棵决策树的集成中,随机深林使用投票的方式来进行分类预测,将每个决策树的预测结果进行投票,得到最终的分类结果。

+ +

在模型训练过程中,随机深林使用梯度提升(Gradient Boosting)算法来更新模型参数。梯度提升算法通过迭代的方式,将每个决策树的预测结果与真实标签之间的误差作为损失函数,更新模型参数,使得模型能够更好地拟合训练数据。

+ +

具体来说,梯度提升算法通过以下步骤来更新模型参数:

+ +
    +
  1. 初始化模型参数。随机深林通常使用浅层决策树作为基分类器,对于每个决策树,需要确定其最大深度、节点分裂的最小样本数等参数。
  2. +
  3. 对于每个决策树,计算其预测结果与真实标签之间的误差。随机深林使用交叉熵损失函数来度量分类预测的误差,使用均方误差(MSE)来度量回归预测的误差。
  4. +
  5. 根据误差计算每个样本的梯度。对于分类问题,样本的梯度是预测结果与真实标签之间的差值,对于回归问题,样本的梯度是预测结果与真实标签之间的差值。
  6. +
  7. 使用梯度下降算法更新模型参数。随机深林使用梯度提升算法来更新模型参数,将每个决策树的预测结果与真实标签之间的误差作为损失函数,通过梯度下降算法来更新模型参数。
  8. +
  9. 重复步骤2-4,直到达到指定的迭代次数或达到收敛条件为止。
  10. +
+ +

在随机深林中,由于每个决策树都是独立训练的,因此可以使用并行化的方式来加速模型训练,提高模型的性能和效率。

+ +

Tell me more.

+ +

随机深林(Random Forest)是一种集成学习算法,它通过组合多个决策树来进行分类和回归预测。随机深林中的每个决策树都是独立训练的,每棵树都是通过随机选择特征和样本进行训练的,从而增强了模型的泛化能力,避免了过拟合问题。

+ +

随机深林的核心思想是基于袋装法(Bagging)和随机特征选择(Random Feature Selection)的组合。具体来说,随机深林采用以下步骤来构建模型:

+ +
    +
  1. 从训练数据集中使用有放回抽样的方式,随机选择一定数量的样本,构建一组新的训练集。
  2. +
  3. 对于每个新的训练集,使用随机特征选择的方式,随机选择一定数量的特征进行训练,构建一棵决策树。
  4. +
  5. 重复步骤1和步骤2,构建多棵决策树,形成随机深林。
  6. +
  7. 对于分类问题,随机深林使用投票的方式来进行分类预测,将每个决策树的预测结果进行投票,得到最终的分类结果;对于回归问题,随机深林使用平均值的方式来进行回归预测,将每个决策树的预测结果进行平均,得到最终的回归结果。
  8. +
+ +

与单棵决策树相比,随机深林具有以下优点:

+ +
    +
  1. 随机深林能够有效地避免过拟合问题,通过组合多个决策树进行预测,能够降低模型的方差,提高模型的泛化能力。
  2. +
  3. 随机深林能够处理高维数据和噪声数据,随机特征选择的方式可以有效地减少冗余特征和噪声特征的影响,从而提高模型的准确性。
  4. +
  5. 随机深林能够并行化处理,每棵决策树都是独立训练的,可以使用多线程或分布式计算的方式加速模型训练,提高模型的效率。
  6. +
+ +

总之,随机深林是一种高效、准确、稳定的集成学习算法,被广泛应用于分类、回归、特征选择等领域。

+ +

更新得到新的参数之后,怎么确定自己的模型已经是最优的呢

+ +

在随机深林中,确定模型是否已经达到最优状态通常需要进行模型评估。模型评估可以通过使用交叉验证方法来进行,常见的交叉验证方法包括K折交叉验证和留一交叉验证。

+ +

K折交叉验证是指将原始数据集分成K份,每次选取其中的K-1份作为训练集,剩余的1份作为验证集,重复K次,得到K个模型的预测结果,最终将K个模型的预测结果进行平均或投票,得到最终的模型预测结果。

+ +

留一交叉验证是指将原始数据集中的每个样本都作为验证集,其余样本作为训练集,重复N次,得到N个模型的预测结果,最终将N个模型的预测结果进行平均或投票,得到最终的模型预测结果。

+ +

在交叉验证过程中,可以使用常见的评估指标来评估模型的性能,如准确率、精确率、召回率、F1值等。通常情况下,选择具有最高评估指标的模型作为最优模型,但也可以根据具体应用场景和需求进行选择。

+ +

此外,还可以使用网格搜索(Grid Search)等方法来进行模型调优,通过在模型参数空间中搜索,找到最优的模型参数组合,从而提高模型的性能和泛化能力。

+ +

总之,在随机深林中,确定最优模型需要进行模型评估和调优,根据具体应用场景和需求进行选择。

+ +

请问如何提高模型的泛化能力?

+ +

在机器学习中,提高模型的泛化能力是非常重要的,因为泛化能力是指模型在未见过的数据上的表现能力,是衡量模型优劣的重要指标之一。以下是提高模型泛化能力的一些方法:

+ +
    +
  1. 增加训练数据量:增加训练数据量可以帮助模型更好地学习数据的特征,从而提高模型的泛化能力。
  2. +
  3. 数据预处理:数据预处理包括数据清洗、特征选择、特征缩放等,可以帮助模型更好地学习数据的特征,从而提高模型的泛化能力。
  4. +
  5. 减少模型复杂度:过于复杂的模型容易过拟合,可以通过减少模型的复杂度来提高模型的泛化能力,如降低模型的层数、节点数等。
  6. +
  7. 正则化:正则化是通过增加模型复杂度惩罚项或限制模型参数的范围来防止过拟合的一种方法,可以提高模型的泛化能力。
  8. +
  9. 集成学习:集成学习是通过组合多个模型来进行预测,可以有效地降低模型的方差,提高模型的泛化能力。
  10. +
  11. Dropout技术:Dropout技术是一种正则化方法,通过在模型训练时随机关闭一定比例的神经元,可以有效地防止模型过拟合,提高模型的泛化能力。
  12. +
  13. 交叉验证:交叉验证是一种通过将数据集划分为多个子集来评估模型性能的方法,可以帮助防止模型过拟合,提高模型的泛化能力。
  14. +
+ +

总之,提高模型的泛化能力需要采取多种方法,包括增加训练数据量、数据预处理、减少模型复杂度、正则化、集成学习、Dropout技术、交叉验证等,根据具体情况选择合适的方法可以有效地提高模型的泛化能力。

+ +

评估模型泛化能力,有什么具体的指标吗

+ +

评估模型泛化能力的常用指标包括:

+ +
    +
  1. 准确率(Accuracy):准确率指分类模型中正确分类的样本数占总样本数的比例,是最常用的指标之一。
  2. +
  3. 精确率(Precision):精确率指被模型预测为正类的样本中,真正为正类的样本比例。
  4. +
  5. 召回率(Recall):召回率指真正为正类的样本中,被模型预测为正类的比例。
  6. +
  7. F1值(F1-score):F1值是精确率和召回率的调和平均数,综合考虑了二者的表现。
  8. +
  9. ROC曲线和AUC值:ROC曲线是以假正率(FPR)为横坐标,真正率(TPR)为纵坐标画出的曲线,用于评估二分类模型的性能。AUC值是ROC曲线下的面积,可以用于比较不同模型的性能。
  10. +
  11. R2值:R2值是用于评估回归模型的性能指标,表示模型预测值与真实值之间的相关性,取值范围为0~1,越接近1表示模型拟合效果越好。
  12. +
  13. 平均绝对误差(MAE)和均方误差(MSE):MAE和MSE是用于评估回归模型的常用指标,分别表示预测值与真实值之间的平均绝对误差和平均平方误差。
  14. +
+ +

总之,评估模型泛化能力需要根据具体的应用场景选择合适的指标,常用的指标包括准确率、精确率、召回率、F1值、ROC曲线和AUC值、R2值、MAE和MSE等

+ +

我有点混淆了,请问性能指标和泛化能力指标的差别

+ +

性能指标和泛化能力指标都是用于评估机器学习模型的指标,但它们的侧重点不同。

+ +

性能指标主要用于评估模型在训练集上的表现,是衡量模型是否能够很好地拟合训练数据的指标。常用的性能指标包括模型的准确率、精确率、召回率、F1值、均方误差(MSE)、平均绝对误差(MAE)等。性能指标可以用于评估模型训练的效果和调整模型参数。

+ +

泛化能力指标主要用于评估模型在未知数据上的表现,即模型是否能够很好地适应新数据。常用的泛化能力指标包括模型的交叉验证结果、测试集上的准确率、AUC值、R2值等。泛化能力指标可以用于评估模型的泛化能力和避免模型的过拟合。

+ +

综上所述,性能指标和泛化能力指标都是用于评估机器学习模型的指标,但是它们的侧重点不同,性能指标主要用于评估模型在训练集上的表现,而泛化能力指标主要用于评估模型在未知数据上的表现。

+ +

如何在模型训练中平衡性能指标和泛化能力指标?

+ +

在模型训练中平衡性能指标和泛化能力指标是一个非常重要的问题。如果只关注性能指标而忽略泛化能力指标,可能会导致模型过拟合,而如果只关注泛化能力指标而忽略性能指标,可能会导致模型欠拟合。以下是一些平衡性能指标和泛化能力指标的方法:

+ +
    +
  1. 数据预处理:数据预处理可以帮助模型更好地学习数据的特征,从而提高模型的性能和泛化能力。例如,可以进行特征选择、特征缩放、数据清洗等操作。
  2. +
  3. 模型选择和调参:选择合适的模型和调整模型参数可以平衡性能指标和泛化能力指标。需要根据实际情况选择合适的模型和调整超参数,避免模型过拟合或欠拟合。
  4. +
  5. 正则化技术:正则化技术可以帮助平衡性能指标和泛化能力指标。例如,L1正则化和L2正则化可以控制模型的复杂度,避免模型过拟合。
  6. +
  7. 交叉验证:交叉验证可以帮助评估模型的泛化能力,避免模型在训练集上过拟合。可以使用K折交叉验证或留一交叉验证等方法。
  8. +
  9. 集成学习:集成学习可以通过组合多个模型的预测结果来提高模型的性能和泛化能力。例如,可以使用Bagging、Boosting、随机森林等方法。
  10. +
+ +

综上所述,平衡性能指标和泛化能力指标需要采取一系列方法,包括数据预处理、模型选择和调参、正则化技术、交叉验证和集成学习等。需要根据实际情况选择合适的方法,从而平衡性能指标和泛化能力指标。

+ +

正则化技术看上去很重要,可以提供更详细的数学定义吗

+ +

正则化是一种常用的降低模型复杂度的技术,它通过在模型的损失函数中添加一个正则项来惩罚模型的复杂度,从而避免模型过拟合。

+ +

在线性回归中,正则化通常采用L1正则化和L2正则化。L1正则化将模型的损失函数定义为:

+ +\[\min_{\theta}\frac{1}{2m}\sum_{i=1}^{m}(h_{\theta}(x^{(i)})-y^{(i)})^2+\lambda\sum_{j=1}^{n}|\theta_j|\] + +

其中,$\theta$是模型的参数,$h_{\theta}(x^{(i)})$是模型对第$i$个样本$x^{(i)}$的预测值,$y^{(i)}$是第$i$个样本的真实值,$m$是样本数,$n$是特征数,$\lambda$是控制正则化强度的超参数。

+ +

L2正则化将模型的损失函数定义为:

+ +\[\min_{\theta}\frac{1}{2m}\sum_{i=1}^{m}(h_{\theta}(x^{(i)})-y^{(i)})^2+\lambda\sum_{j=1}^{n}\theta_j^2\] + +

L1正则化和L2正则化的区别在于正则项的形式不同。L1正则化的正则项是所有参数的绝对值之和,它倾向于将一些参数压缩到0,从而产生稀疏性,可以用于特征选择和模型压缩;而L2正则化的正则项是所有参数的平方和,它倾向于将所有参数都缩小,从而产生平滑性。

+ +

在深度学习中,正则化也是一种常用的技术。例如,Dropout正则化可以随机地将一些神经元输出置为0,从而减少了神经元之间的依赖性,避免模型过拟合。Batch Normalization正则化可以通过对每个批次的输入数据进行标准化,从而减少了模型对输入数据的依赖性,提高了模型的泛化能力。

+ +

综上所述,正则化是一种常用的降低模型复杂度的技术,可以通过在模型的损失函数中添加一个正则项来惩罚模型的复杂度,从而避免模型过拟合。常用的正则化技术包括L1正则化、L2正则化、Dropout正则化和Batch Normalization正则化等。

+ +

如何部署到实际的应用中呢

+ +

部署机器学习模型到实际的应用中需要考虑多个方面,包括模型选择、数据预处理、模型训练、模型评估、模型部署、性能优化和安全性等问题。以下是一些常用的部署机器学习模型的方法:

+ +
    +
  1. 模型选择:需要根据实际情况选择合适的模型,并进行模型训练和评估。可以使用开源的机器学习库,如Scikit-learn、TensorFlow、PyTorch等。
  2. +
  3. 数据预处理:需要对输入数据进行预处理,包括数据清洗、特征提取、特征缩放等操作。可以使用Pandas、NumPy等库进行数据处理。
  4. +
  5. 模型训练和评估:需要进行模型训练和评估,选择合适的算法、超参数和评估指标。可以使用交叉验证、网格搜索等技术进行模型优化。
  6. +
  7. 模型部署:需要将训练好的模型部署到实际的应用中。可以使用各种部署技术,如Docker、Kubernetes等。
  8. +
  9. 性能优化:需要对模型进行性能优化,包括模型压缩、量化、加速等操作。可以使用TensorRT、TensorFlow Lite等库进行性能优化。
  10. +
  11. 安全性:需要保证模型的安全性,包括数据隐私、模型盗用等问题。可以使用加密、数据脱敏、模型水印等技术保证模型的安全性。
  12. +
+ +

综上所述,部署机器学习模型到实际的应用中需要考虑多个方面,包括模型选择、数据预处理、模型训练、模型评估、模型部署、性能优化和安全性等问题。需要根据实际情况选择合适的技术和方法,从而实现高性能、高可靠性和高安全性的部署。

+ +

需要将训练好的模型部署到实际的应用中。可以使用各种部署技术,如Docker、Kubernetes等。请问可以详细一点吗

+ +

当需要将训练好的机器学习模型部署到实际的应用中时,通常会选择使用一些容器化技术,如Docker和Kubernetes。这些技术可以帮助开发人员更轻松地管理和部署应用程序和服务,并提供一些额外的好处,例如可扩展性、安全性和可靠性等。

+ +

下面是一些常用的容器化技术和部署方法:

+ +
    +
  1. Docker:Docker是一种流行的开源容器化平台,可以将应用程序和服务打包成容器,从而实现快速、可重复和可移植的部署。可以使用Dockerfile文件定义容器镜像,然后使用Docker命令构建和运行容器。
  2. +
  3. Kubernetes:Kubernetes是一个开源的容器编排平台,可以管理容器化应用程序和服务的部署、扩展、升级和滚动更新等操作。可以使用Kubernetes的配置文件定义应用程序和服务的部署和管理。
  4. +
  5. 容器编排工具:除了Kubernetes之外,还有一些其他的容器编排工具,如Docker Compose、Apache MesosHashiCorp Nomad等。这些工具可以帮助开发人员更轻松地管理和部署多个容器化应用程序和服务。
  6. +
  7. 云计算平台:云计算平台,如AWS、Azure、GCP等,提供了各种容器化服务和工具,可以帮助开发人员更轻松地部署和管理容器化应用程序和服务。
  8. +
+ +

综上所述,将训练好的机器学习模型部署到实际的应用中,可以使用容器化技术,如Docker和Kubernetes等。这些技术可以帮助开发人员更轻松地管理和部署应用程序和服务,并提供一些额外的好处,例如可扩展性、安全性和可靠性等。需要根据实际情况选择合适的技术和方法,从而实现高性能、高可靠性和高安全性的部署。

+ +

可以把所有的基于机器学习的风机轴承健康监测评估的步骤列一下吗

+ +

基于机器学习的风机轴承健康监测评估通常包括以下步骤:

+ +
    +
  1. 数据收集:收集与风机轴承相关的传感器数据,例如振动、温度、压力等。
  2. +
  3. 数据预处理:对收集到的数据进行预处理,包括数据清洗、去噪、滤波、降采样等操作,以提高数据质量和降低数据维度。
  4. +
  5. 特征提取:从预处理后的数据中提取重要的特征,例如时域特征、频域特征、小波特征等。可以使用相关统计学方法、信号处理技术和机器学习算法进行特征提取。
  6. +
  7. 特征选择:从提取的特征中选择最具有代表性和预测能力的特征,以减少特征维度和提高模型性能。可以使用相关统计学方法、机器学习算法和特征重要性评估等技术进行特征选择。
  8. +
  9. 模型训练:使用机器学习算法训练模型,例如支持向量机、随机森林、神经网络等。可以使用交叉验证、网格搜索等技术进行模型选择和优化。
  10. +
  11. 模型评估:对训练好的模型进行评估,例如精度、召回率、F1分数等。可以使用混淆矩阵、ROC曲线、AUC值等指标进行模型评估。
  12. +
  13. 模型部署:将训练好的模型部署到实际的应用中,例如使用Docker、Kubernetes等技术进行容器化部署。
  14. +
  15. 实时监测:在实际使用中,对风机轴承进行实时监测,获取新的传感器数据,进行数据预处理、特征提取、特征选择和模型预测等操作,以实现实时健康监测和预测。
  16. +
+ +

综上所述,基于机器学习的风机轴承健康监测评估通常包括数据收集、数据预处理、特征提取、特征选择、模型训练、模型评估、模型部署和实时监测等步骤。需要根据实际情况选择合适的技术和方法,从而实现高性能、高可靠性和高安全性的风机轴承健康监测和预测。

+ +

有具体别人做过的案例可以学习吗,比如说发布的文章或者博客

+ +

是的,有很多机器学习的案例可以学习,以下是一些基于机器学习的风机轴承健康监测评估的案例,供您参考:

+ +
    +
  1. “A Deep Convolutional Neural Network Based Method for Fault Diagnosis of Wind Turbine Bearings”(基于深度卷积神经网络的风力涡轮机轴承故障诊断方法):该论文提出了一种基于深度卷积神经网络的轴承故障诊断方法,在实验中取得了良好的效果。论文链接:https://ieeexplore.ieee.org/document/8353225
  2. +
  3. “Wind turbine gearbox fault diagnosis based on PCA and SVM“(基于PCA和SVM算法的风力涡轮机齿轮箱故障诊断):该论文提出了一种基于主成分分析和支持向量机算法的齿轮箱故障诊断方法,在实验中取得了良好的效果。论文链接:https://www.sciencedirect.com/science/article/pii/S1876610213002019
  4. +
  5. “Wind turbine fault detection using machine learning algorithms“(利用机器学习算法进行风力涡轮机故障检测):该论文提出了一种基于K近邻算法和支持向量机算法的风力涡轮机故障检测方法,在实验中取得了良好的效果。论文链接:https://www.sciencedirect.com/science/article/pii/S0378779616302521
  6. +
  7. “Wind Turbine Fault Detection and Diagnosis Based on Machine Learning Techniques“(基于机器学习技术的风力涡轮机故障检测和诊断):该博客介绍了一种基于机器学习技术的风力涡轮机故障检测和诊断方法,包括数据预处理、特征提取、特征选择、模型训练和评估等步骤。博客链接:https://towardsdatascience.com/wind-turbine-fault-detection-and-diagnosis-based-on-machine-learning-techniques-52c0b4f4b4e8
  8. +
+ +

以上是一些基于机器学习的风机轴承健康监测评估的案例,希望对您有所帮助。

+ +

你提供的链接全都是错误的

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/dd/486be81de2456668b05959dd40bbc9d824be82f68b42b3aabcb329b3cf6498 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/dd/486be81de2456668b05959dd40bbc9d824be82f68b42b3aabcb329b3cf6498 new file mode 100644 index 0000000000..80c4e3565e --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/dd/486be81de2456668b05959dd40bbc9d824be82f68b42b3aabcb329b3cf6498 @@ -0,0 +1,2 @@ +I"

介绍了Allure的安装,环境配置;Allure命令,配置pytest.ini文件,利用allure工具生成html报告;最后,讲了Allure的应用实例。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/dd/8f09a12c740b832964e70777ff42929f7dc09119bf0f81be701f3a9fdf8d0d b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/dd/8f09a12c740b832964e70777ff42929f7dc09119bf0f81be701f3a9fdf8d0d new file mode 100644 index 0000000000..3776c51ae9 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/dd/8f09a12c740b832964e70777ff42929f7dc09119bf0f81be701f3a9fdf8d0d @@ -0,0 +1,44 @@ +I"2 + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e1/31f876cd92c281f2badda6be08ddd39a7ec0e7772532bcdac86388d303fbba b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e1/31f876cd92c281f2badda6be08ddd39a7ec0e7772532bcdac86388d303fbba new file mode 100644 index 0000000000..35b6ba2983 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e1/31f876cd92c281f2badda6be08ddd39a7ec0e7772532bcdac86388d303fbba @@ -0,0 +1,18 @@ +I"T + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e2/a06b550f880b5f003d487c29ea2f8fa57a3ccd444264c1bd5777aa04501040 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e2/a06b550f880b5f003d487c29ea2f8fa57a3ccd444264c1bd5777aa04501040 new file mode 100644 index 0000000000..1977affca1 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e2/a06b550f880b5f003d487c29ea2f8fa57a3ccd444264c1bd5777aa04501040 @@ -0,0 +1,23 @@ +I"j + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e3/5197203d6200fee8b0e241535efac12ce0301761f47d6d1dcfff779d24b46f b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e3/5197203d6200fee8b0e241535efac12ce0301761f47d6d1dcfff779d24b46f new file mode 100644 index 0000000000..9d27822a95 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e3/5197203d6200fee8b0e241535efac12ce0301761f47d6d1dcfff779d24b46f @@ -0,0 +1,2 @@ +I"q

字符串是指由零个或多个字符组成的有限序列,是编程语言中最常用的数据类型之一。字符串是由单引号或双引号括起来的任意文本,比如’abc’,”xyz”等等。请注意,’‘或”“本身只是一种表示方式,不是字符串的一部分,因此,字符串’abc’只有a,b,c这3个字符。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e3/b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e3/b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 new file mode 100644 index 0000000000..e135808ffe --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e3/b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 @@ -0,0 +1,2 @@ +I" +:EF \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e5/b61562c4bec5675f8d2d17bfe8270831f4aeae3d306ca2b583a5caa9ef0b6c b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e5/b61562c4bec5675f8d2d17bfe8270831f4aeae3d306ca2b583a5caa9ef0b6c new file mode 100644 index 0000000000..5cc45fb977 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e5/b61562c4bec5675f8d2d17bfe8270831f4aeae3d306ca2b583a5caa9ef0b6c @@ -0,0 +1,2 @@ +I"

主要包括requests库的安装,接口用例编写,requests库的使用,requests库的返回值介绍,requests库的用例编写以及Requests的封装等等。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e6/0d90ec63542ec055b8f79c8be81c0773538753c4a4726392242ce3b09bde4e b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e6/0d90ec63542ec055b8f79c8be81c0773538753c4a4726392242ce3b09bde4e new file mode 100644 index 0000000000..ed2ac2ca88 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e6/0d90ec63542ec055b8f79c8be81c0773538753c4a4726392242ce3b09bde4e @@ -0,0 +1,2 @@ +I"F

断言是一种判断程序执行结果是否符合预期的方法,可以帮助我们定位错误原因,是自动化测试的灵魂。本文主要介绍pytest中使用assert进行断言,断言的应用和封装。断言结果的验证,包括状态码、返回信息验证,以及数据库断言结果验证。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e7/4d8a7ed08d57597132f9bb6ad381a9d133693e9709174c1cda2243f85a3ee0 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e7/4d8a7ed08d57597132f9bb6ad381a9d133693e9709174c1cda2243f85a3ee0 new file mode 100644 index 0000000000..c359fc8131 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e7/4d8a7ed08d57597132f9bb6ad381a9d133693e9709174c1cda2243f85a3ee0 @@ -0,0 +1,2 @@ +I"

介绍python中引用与对象,可变数据类型与不可变数据类型,值传递,变量和内存管理等方面的内容

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e7/961aab13cec6f759467edbb24cc556136af4826b5fefbb84d76680c7784c12 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e7/961aab13cec6f759467edbb24cc556136af4826b5fefbb84d76680c7784c12 new file mode 100644 index 0000000000..2e06c1c1d7 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e7/961aab13cec6f759467edbb24cc556136af4826b5fefbb84d76680c7784c12 @@ -0,0 +1,71 @@ +I"\

软件包管理是任何Linux发行版的重要功能之一,可简化Linux应用程序的安装和维护方法。不同的Linux发行版采用不同的方法来打包和分发软件。

+ +

但是对于某些切换到不同的Linux发行版来的人说,相同的功能有时反而会成为绊脚石。他们发现很难理解新的软件包管理器,并且无法安装应用程序。为了使用多个程序包管理器解决此类问题,Linux发行版已经发展出了通用的包管理系统,如Snap、Appimage和Flatpak。

+ +

什么是Flatpak?

+ +

Flatpak是一个通用的软件包管理系统,用于在任何Linux发行版上构建和分发应用程序。您无需学习特定于发行版的软件包管理器即可安装Flatpak应用。它为所有Linux发行版提供了一个命令行实用程序,以下载,安装和更新该应用程序。

+ +

什么是Flathub?

+ +

Flathub 是一个包含了几乎所有 flatpak 应用的仓库,可为Linux系统提供大量的应用程序和游戏。它还为想要构建,分发和定期更新应用程序的开发人员提供了构建服务。

+ +

设置Flatpak

+ +
# 安装flatpak
+sudo apt install flatpak
+# 安装图形化界面,但是我没有安装成功
+sudo apt install gnome-software-plugin-flatpak
+# 添加flathub仓库
+flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
+
+ +

从Flathub商店下载和安装Flatpak应用程序

+ +

安装和运行OBS

+ +

从Flathub商店下载

+ + + +

搜索安装

+ +
flatpak search obs
+flatpak install <app id>
+flatpak run <app id>
+
+ +

查看安装结果

+ +
flatpak list
+
+ +

已经安装的应用

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e8/6ec102b9044b4c9b243c33489b4e4f2ad40a01a52a7e093a0834226ea21f3e b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e8/6ec102b9044b4c9b243c33489b4e4f2ad40a01a52a7e093a0834226ea21f3e new file mode 100644 index 0000000000..5f7193c599 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e8/6ec102b9044b4c9b243c33489b4e4f2ad40a01a52a7e093a0834226ea21f3e @@ -0,0 +1,166 @@ +I"

单元测试

+ +

单元测试的英文是Unit Testing,特指在一个分隔的代码单元中进行的测试,一个单元可以是整个模块,一个单独的类或者函数。

+ +

编写单元测试的代码可以帮助我们在早期快速找到bug,以较小的代价在早期找到bug。

+ +

被测函数

+ +

我们先编写一个函数,判断输入数据是否是素数,并且可以判断距离这个数最近的素数。

+ +
.
+# Author: chenjunllin
+# Date: 2022-03-16 
+# File: is_prime.py
+
+
+def is_prime(number):
+    """如果是质数,返回为True"""
+    for element in range(2, number):
+        if number % element == 0:
+            return False
+    return True
+
+
+def print_next_prime(number):
+    """打印输入number数字最近的质数"""
+    index = number
+    while True:
+        index += 1
+        if is_prime(index):
+            print(index)
+
+ +

测试is_prime函数

+ +

使用测试驱动开发(TDD)的方法,编写的最简单的测试代码测试is_prime函数是否实现了功能,代码如下:

+ +

+# Author: chenjunlin
+# Date: 2023-03-16
+# File: test_is_prime.py
+
+import pytest
+
+from src.chapter7unit.method_examples.is_prime import is_prime
+
+
+class TestPrimes(object):
+    """测试is_prime"""
+
+    def test_is_zero_not_prime(self):
+        """测试边界值0是不是质数"""
+        assert is_prime(0) is False
+
+    def test_is_one_not_prime(self):
+        """测试边界值1是不是质数"""
+        assert is_prime(1) is False
+
+    def test_is_two_prime(self):
+        """2是质数"""
+        assert is_prime(2) is True
+
+    def test_is_four_non_prime(self):
+        """4不是质数"""
+        assert is_prime(4) is False
+
+    def test_is_five_prime(self):
+        """5是不是质数?"""
+        assert is_prime(5) is True
+
+
+if __name__ == '__main__':
+    pytest.main()
+
+
+ +

输出结果如下:

+ +

也就是说,0和1不是素数,但是函数返回的结果是1,说明is_prime函数没有实现功能。 +20230316105600

+ +

回到原来的函数,发现是因为range函数的范围是从2到number,所以0和1都没有被判断,所以我们修改一下代码,如下:

+ +
def is_prime(number):
+    """如果是质数,返回为True"""
+    if number < 2:
+        return False
+    for element in range(2, number):
+        if number % element == 0:
+            return False
+    return True
+
+ +

再次运行test_is_prime.py测试代码,发现测试通过了。

+ +

20230316105849

+ +

测试print_next_prime函数

+ +

我们再来编写一个测试函数,测试print_next_prime函数,代码如下:

+ +
# Author: chenjunlin
+# Date: 2023-03-16
+# File: test_print_is_prime.py
+
+import pytest
+
+from src.chapter7unit.method_examples.is_prime import print_next_prime
+
+class TestPrimes(object):
+    """测试print_next_prime"""
+
+    def test_print_next_prime_0(self):
+        """ 0的最近质数是2 """
+        assert print_next_prime(0) is 2
+
+    def test_print_next_prime_4(self):
+        """ 4的最近质数是5 """
+        assert print_next_prime(4) is 5
+
+    def test_print_next_prime_5(self):
+        """ 5的最近质数是7 """
+        assert print_next_prime(5) is 7
+
+    def test_print_next_prime_7(self):
+        """ 7的最近质数是11 """
+        assert print_next_prime(7) is 11
+
+
+if __name__ == '__main__':
+    pytest.main()
+
+ +

没有通过测试,结果如下:

+ +

1678936036418

+ +

原来是因为print_next_prime函数返回结果并不是距离输入数字最近的素数,而是返回了所有大于输入数字的素数,所以在running tests中,我们可以发现一直在打转,没有返回assert断言的结果。

+ +

我们还可以通过在is_prime.py中添加魔术方法__name__ == ‘main‘来测试print_next_prime函数,代码如下:

+ +
if __name__ == '__main__':
+
+    print(is_prime(2))
+    print_next_prime(0)
+
+
+ +

发现结果确实是打印了所有大于输入数字的素数:

+ +

1678936579647

+ +

我们修改一下代码,如下:

+ +
def print_next_prime2(number):
+    """打印输入number数字最近的质数"""
+    index = number
+    while True:
+        index += 1
+        if is_prime(index):
+            return index
+
+ +

再次运行测试代码,发现测试通过了。

+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e9/a63e64ea6263849bc7c2fc92752c654ea00c64fa2583cf63aa87ce28470916 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e9/a63e64ea6263849bc7c2fc92752c654ea00c64fa2583cf63aa87ce28470916 new file mode 100644 index 0000000000..9658ecbf1c --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e9/a63e64ea6263849bc7c2fc92752c654ea00c64fa2583cf63aa87ce28470916 @@ -0,0 +1,16 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e9/e4496eb136082ba0cc40b726b7f1d7a018f86a57f56c5092ce4317ed9e612c b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e9/e4496eb136082ba0cc40b726b7f1d7a018f86a57f56c5092ce4317ed9e612c new file mode 100644 index 0000000000..988accbe13 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e9/e4496eb136082ba0cc40b726b7f1d7a018f86a57f56c5092ce4317ed9e612c @@ -0,0 +1,27 @@ +I"e + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e9/f9dc47b44c68b25f1c714e9a64b3f33204369a0efb6965cba56f0bb3f732f4 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e9/f9dc47b44c68b25f1c714e9a64b3f33204369a0efb6965cba56f0bb3f732f4 new file mode 100644 index 0000000000..54dd7b37ba --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/e9/f9dc47b44c68b25f1c714e9a64b3f33204369a0efb6965cba56f0bb3f732f4 @@ -0,0 +1,10 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ea/d0d98217b213bb2621eecb66afac0f3744100575b7e3132d0d505c4a769294 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ea/d0d98217b213bb2621eecb66afac0f3744100575b7e3132d0d505c4a769294 new file mode 100644 index 0000000000..2b45d09f7e --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ea/d0d98217b213bb2621eecb66afac0f3744100575b7e3132d0d505c4a769294 @@ -0,0 +1,13 @@ +I"s + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/eb/922c261846befa764ead7a768aac96392215ca9bd7c78dc8d807d224b38293 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/eb/922c261846befa764ead7a768aac96392215ca9bd7c78dc8d807d224b38293 new file mode 100644 index 0000000000..3b0eb2fdc9 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/eb/922c261846befa764ead7a768aac96392215ca9bd7c78dc8d807d224b38293 @@ -0,0 +1,24 @@ +I"2 + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/eb/9cb95107d59a9c0651a22e42aa25c08b9fcb5d43c6d6b2450f0146a80f5101 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/eb/9cb95107d59a9c0651a22e42aa25c08b9fcb5d43c6d6b2450f0146a80f5101 new file mode 100644 index 0000000000..a870680019 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/eb/9cb95107d59a9c0651a22e42aa25c08b9fcb5d43c6d6b2450f0146a80f5101 @@ -0,0 +1,2 @@ +I"

主要包括日志文件介绍以及快速使用,文件封装,日志配置文件重构,日志工具类应用等等内容。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ef/42dd0b5037e9e5097e6cc18edc3d37f398e616808339ef307e9c7fe87b2235 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ef/42dd0b5037e9e5097e6cc18edc3d37f398e616808339ef307e9c7fe87b2235 new file mode 100644 index 0000000000..d56bf3d993 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ef/42dd0b5037e9e5097e6cc18edc3d37f398e616808339ef307e9c7fe87b2235 @@ -0,0 +1,2 @@ +I"g

介绍了邮件配置的目的,邮件配置的方法,邮件封装以及邮件运行。

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ef/735e951df7da8fe87ca7a2c12db8e5217748441d490e8adb2dd41e24de8c40 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ef/735e951df7da8fe87ca7a2c12db8e5217748441d490e8adb2dd41e24de8c40 new file mode 100644 index 0000000000..780aae01df --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/ef/735e951df7da8fe87ca7a2c12db8e5217748441d490e8adb2dd41e24de8c40 @@ -0,0 +1,2 @@ +I"

美多商城接口自动化测试项目框架代码

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/f0/fd74681aaa4d6ef9dba552d51dafe77d7a58926919e454bc577db3b3ccadd6 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/f0/fd74681aaa4d6ef9dba552d51dafe77d7a58926919e454bc577db3b3ccadd6 new file mode 100644 index 0000000000..ac5f557e73 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/f0/fd74681aaa4d6ef9dba552d51dafe77d7a58926919e454bc577db3b3ccadd6 @@ -0,0 +1,2 @@ +I"-

准备工作

+:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/f1/1632c41d0180fdcd2843af8d2ea5f000e14778f6d2cbae55f33c989384d5b7 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/f1/1632c41d0180fdcd2843af8d2ea5f000e14778f6d2cbae55f33c989384d5b7 new file mode 100644 index 0000000000..6172802bdb --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/f1/1632c41d0180fdcd2843af8d2ea5f000e14778f6d2cbae55f33c989384d5b7 @@ -0,0 +1,7 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/f6/c60f72d31287e9364ea72d7bd37e44eeb824addd9e6bcb68b6d001cca1dd53 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/f6/c60f72d31287e9364ea72d7bd37e44eeb824addd9e6bcb68b6d001cca1dd53 new file mode 100644 index 0000000000..c759ab9be8 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/f6/c60f72d31287e9364ea72d7bd37e44eeb824addd9e6bcb68b6d001cca1dd53 @@ -0,0 +1,368 @@ +I"8

linux系统的网络连接状态

+ +

LINUX网络设置

+

这一节主要讲如何查看并修改如主机名,路由器,网卡,网络连接方式等等。

+ +

1.查看网络配置

+ +

查看所有活动的网络接口

+ +
ifconfig -a
+
+ +

ens33:第一块以太网卡的名称,”ens33”中的”en”是”“EtherNet””的缩写,表示网卡类型为以太网,”s”表示热插拔插槽上的设备(hot-plugslot.·数字“33”表示插槽编号。

+ +
ifconfig ens33
+inet 192.168.245.211 #ip地址
+netmask 255.
+broadcast 192.168.245.255  #广播地址
+inet6
+ether 00:0c:29:5a:5a:5a #mac地址
+
+ +

2.查看网络状态

+ +
netstat -tunlp
+# -t:查看TCP (Transmission Control Protocol,传输控制协议)相关的信息
+# -u:查看UDP (User Datagram Protocol,用户数据报协议)相关的信息
+# -n:显示数字格式的IP地址(主机地址、端口信息)
+# -l:显示监听套接字(Listenin状态的网络连接及端口信息)
+# -p:显示进程信息(该选项需要root权限)
+
+ +

3. 临时修改主机名

+ +
hostnamectl set-hostname ### 4.永久修改主机名
+
+vim /etc/hostname
+vim /etc/hosts
+
+ +

5.测试网络连通性

+ +
ping www.baidu.com
+ping -c 3 www.baidu.com #测试3次 
+ping -c 3 -i 0.2 www.baidu.com #每次间隔0.2秒
+ping -c 3 -i 0.2 -w 1 www.baidu.com #超时时间为1秒(只ping五秒,5秒之后结束)
+
+ +

一般来说,网络不通,用ping来定位问题节点的位置,ping的顺序如下:

+ +
ping 127.0.0.1 #ping 本地回环,测试本地网络协议是否正常
+ping 192.168.0.31 #ping 本地IP,测试本地网络接口是否正常
+ping 192.168.0.254 # ping 网关,测试网关是否工作正常
+ping 202.106.0.20 # ping 外部网络,测试服务商网络是否工作正常
+
+ +

6.查看路由表

+ +
    route -n
+    # -n:以数字格式显示路由表信息
+    # -v:显示详细的路由表信息
+    # -e:显示路由表中的错误信息
+    # -A:显示所有的路由表信息
+    # -g:显示网关信息
+    # -C:显示缓存信息
+    # -F:显示过滤信息
+    # -M:显示多播信息
+    # -N:显示网络信息
+    # -S:显示套接字信息
+    # -T:显示TCP信息
+    # -U:显示UDP信息
+    # -V:显示版本信息
+    # -W:显示网卡信息
+    # -X:显示X.25信息
+    # -Z:显示SCTP信息
+
+ +

7.查看DNS服务器

+ +
cat /etc/resolv.conf
+#nameserver
+#测试DNS服务器是否可用
+nslookup www.baidu.com #目标主机地址 
+# 解析DNS信息收集
+dig www.baidu.com
+# 本地主机映射文件
+# etc/hosts 文件中记录着一份主机名与 IP 地址的映射关系表,一般用来保存经常需要访问的主机的信息。
+# 当访问一个未知的域名时,先查找该文件中是否有相应的映射记录,如果找不到再去向DNS 服务器查询
+cat /etc/hosts
+# 本地主机名解析文件
+# /etc/nsswitch.conf 文件中记录着本地主机名解析的顺序,一般情况下,本地主机名解析的顺序为:
+# hosts > dns > files > nis > nisplus > db > ldap > compat > hesiod > myhostname
+
+ +

8.查看网卡信息

+ +
    ifconfig
+    # -a:显示所有网卡信息
+    # -s:显示网卡的简要信息
+    # -d:显示网卡的详细信息
+    # -v:显示网卡的详细信息
+    # -r:显示路由信息
+    # -m:显示多播信息
+    # -u:显示UDP信息
+    # -t:显示TCP信息
+    # -x:显示X.25信息
+    # -z:显示SCTP信息
+
+ +

9.网络连接方式

+ +

动态网络dhcp

+ +
网桥方式
+

link: https://blog.csdn.net/qq_59323083/article/details/125531699

+ +
    # 1.安装网桥工具
+    yum install bridge-utils
+    # 2.配置网桥
+    vim /etc/sysconfig/network-scripts/ifcfg-eth0
+    # 添加以下内容
+    DEVICE=eth0
+    BOOTPROTO=dhcp
+    ONBOOT=yes
+    BRIDGE=br0
+    # 3.配置网桥
+    vim /etc/sysconfig/network-scripts/ifcfg-br0
+    # 添加以下内容
+    DEVICE=br0
+    BOOTPROTO=static
+    IPADDR=
+
+ +
NAT方式
+

打开虚拟网络编辑器,所有数据不要去改动,如下图所示: +虚拟网络编辑器 +打开虚拟机设置,网络适配器,选择NAT模式,然后点击高级,选择端口转发,添加端口转发规则,如下图所示: +NAT模式 +重启网络

+ +
systemctl restart network
+
+ +

检查网络连通性

+ +
    ping www.baidu.com
+
+ +

静态网络

+ +
网桥模式联网(static)
+ +

通过VMware编辑菜单栏点击虚拟网络编辑器,设置VMnet0,选择桥接模式,外部连接不要选自动,而是选择你的宿主主机的网卡,考虑到一般有有线和无线两块网卡,但这里选择的这块网卡是连接Internet的,比如,这个图中所示,我使用无线网卡连接到Internet,所以选择它,然后“确定”。(如图所示) +设置VMnet0 +虚拟机 设置 网络适配器 选择桥接模式,如下图所示: +网桥模式联网 +修改配置文件/etc/sysconfig/network-scripts/ifcfg-ens33 +修改配置文件 +重启网络服务

+ +
systemctl restart network
+
+ +
NAT模式联网(static)
+ +

通过VMware编辑菜单栏点击虚拟网络编辑器,选中VMnet8点击NAT设置: +选中VMnet8点击NAT设置 +不使用本地DHCP服务,这样就得勾消DHCP选项,然后查看DHCP确保未启用。 +不使用本地DHCP服务

+ +

第四步,打开NAT设置,记录下图中表示的数据用于修改配置文件: +NAT设置和配置

+ +

第五步,打开虚拟机设置,配置网络适配器,如下图所示: +配置网络适配器 +第六步,修改配置文件/etc/sysconfig/network-scripts/ifcfg-ens33,修改的时候必须按第四步的IP网段和网关修改,具体根据你自己的情况决定这个网段,比如,我的网段是192.168.75.0,网关是192.168.75.2,子网掩码是255.255.255.0。 +修改配置文件 +常见配置

+ +

第七步,重启服务:

+ +
systemctl restart network
+
+ +

配置DNS

+ +

第一步:修改配置文件 /etc/resolv.conf ,如果你公司有自己的DNS服务器,那么用本公司的,也可以到网上查找免费的DNS服务器,比如:

+ +

中国互联网络中心:1.2.4.8、210.2.4.8、101.226.4.6(电信及移动)、123.125.81.6(联通) +阿里DNS:223.5.5.5、223.6.6.6 +googleDNS:8.8.8.8、8.8.4.4 +openDNS:208.67.222.222208.67.220.220208.67.222.220208.67.220.222 +可以阻挡含有恶意网站的dns +208.67.220.123

+ +

无劫持 去广告或粗俗网站dns:114.114.114.114 和 114.114.115.115

+ +

拦截钓鱼病毒木马网站,可增强网银、证券、购物、游戏、隐私信息安全的DNS 114.114.114.119 和 114.114.115.119

+ +

然后修改配置文件 /etc/resolv.conf,如下表所示: +修改配置文件

+ +

可最多写3个

+ +

当你配好resolv.conf文件后,过了一会,发现刚才配置好的DNS又失效了,原来DNS被系统重新覆盖或者被清除!所以你还必须完成以下工作。

+ +

第二步:修改/etc/NetworkManager/NetworkManager.conf文件

+ +

修改NetWorkManager.conf文件

+ +

网络基本管理–与网络配置相关的常用命令

+ +
    +
  1. +

    查看网络配置信息 +ifconfig 的功能是管理网络接口,用于查看、配置、启用或禁用网络接口。

    + +
     ifconfig interface [aftype] options | address …
    +
    + +

    ifconfig 命令的主要参数

    +
  2. +
+ +

ifconfig命令是Linux下用来配置和显示网络接口信息的命令,它可以显示网络接口的详细信息,包括IP地址、子网掩码、广播地址、MAC地址等。

+ +
ifconfig
+#安装ifconfig命令
+yum -y install net-tools
+# 从源头开始安装
+yum -y install net-tools.x86_64
+
+ +

ifconfig语法

+ +
    ifconfig [选项] [接口名]
+    ifconfig [选项] [接口名] [地址] [广播地址] [子网掩码] 网卡控制命令
+
+ifconfig eth0 up # 启动网卡
+ifconfig eth0 down # 关闭网卡
+ifconfig eth0
+ifup eth0 # 启动网卡
+ifup ens33
+ifup eth0:0 # 启动虚拟网卡
+ifdown ens33
+
+ +

nmcli命令 +该命令可以完成网卡上所有的配置工作,并且可以写入配置文件,重启后依然有效。

+ +
nmcli device show ens33 # 查看网卡信息
+nmcli device status # 查看网卡状态
+nmcli device disconnect ens33 # 断开网卡
+nmcli device connect ens33 # 连接网卡
+nmcli device modify ens33 ipv4.addresses
+nmcli device modify ens33 ipv4.addresses
+
+ +

检查网卡知否连接网线的命令

+ +
ethtool ens33 # 查看网卡信息
+ethtool ens33 | grep "Link detected" # 查看网卡是否连接网线
+
+ +
    +
  1. +

    ping命令

    + +
     ping [options] hostname/ip
    +
    + +

    ping命令

    +
  2. +
  3. +

    检查网络状态(netstat)

    + +
     netstat [-veenNcCF] [<Af>] -r
    +
    +
  4. +
+ +

ens33是我们用于连网的网卡,其功能大多已经了解;下面我们着重介绍一下lo和virbr0的功能。

+

关于lo

+ +

lo(lo: local loopback的简写)是一个虚拟网络接口。所谓虚拟网络接口,是指不从外界接收和发送数据包,只在系统内部接收和发送数据包,不需要驱动程序。

+ +

lo接口的作用是为本地不同进程间进行数据交换提供服务。直观地说lo就是回环地址127.0.0.1,用于本机通信,数据经过内核处理后直接返回,不会在任何网络中出现。

+ +

利用命令ping 127.0.0.1可以检查本地TCP/IP协议以及基本数据接口等是否工作正常。

+ +

观察本机间不同进程交换数据包的过程,可以使用命令tcpdump -i lo。

+ +
tpcpdump -i lo
+
+ +

也可以用WireShark进行抓包,选择lo网卡即可。

+ +
yum -y install wireshark -gnome # 安装wireshark 带有gnome界面
+
+ +

然后再终端输入 wireshark 即可打开wireshark +wireshark界面

+ +

关于virbr0

+ +

在虚拟机下安装Linux,这样便会看到vibro这个网络设备,其中的IP地址,子网掩码等都是虚拟机控制程序分配的。

+ +

virbr0其实就是一个虚拟网桥(virtual bridge),你也可以理解为在虚拟网络中的交换机,当你在VMware Workstation下安装了多个虚拟机时,这些虚拟机之间就是通过这个交换机virbr0互联的,没有virbr0,各虚拟机之间无法通信。尽管你的虚拟机与virbr0的IP地址192.168.122.1不属于同一网段,但ping 192.168.122.1是通的,你不妨试试ping一下。

+ +

时间与网络时间同步

+ +

使用date命令查看之后,发现时间与本地时间不一致。 +NTP(Network Time Protocol)是网络时间协议,是用来同步网络中各个计算机时间的协议。

+ +
    +
  1. +

    安装ntpdate

    + +
     yum -y install ntpdate
    + yum install ntp ntp-doc ntpdate # 安装ntp
    + systemctl start ntpd # 启动ntp服务
    + systemcl enable ntpd # 设置开机启动
    +
    +
  2. +
+ +

需要提醒的是,每当修改配置文件后,都需要重启NTP 服务。

+ +
    +
  1. +

    同步时间

    + +

    ntpdate后面可以选择

    + +

    1.asia.pool.ntp.org、 +2.asia.pool.ntp.org、 +3.asia.pool.ntp.org

    + +

    也可以选择其他国家的ntp服务器,比如美国的ntp服务器为

    + +

    0.us.pool.ntp.org、 +1.us.pool.ntp.org、 +2.us.pool.ntp.org、 +3.us.pool.ntp.org。

    + +
     ntpdate cn.pool.ntp.org
    + date #系统时间和本地时间一致
    +
    +
  2. +
  3. +

    将系统时间写入硬件时间

    + +
     hwclock --systohc
    + timedatectl
    +
    +
  4. +
  5. +

    强制系统时间写入CMOS中防止重启失效

    + +
    hwclock -w #将系统时间写入CMOS中
    +hwclock -s # 将CMOS时间写入硬件
    +hwclock --show # 查看硬件时间
    +hwclock -r # 查看CMOS时间
    +
    +
  6. +
+ +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/f7/80903e237784afa195dc34276fd42d3ec1a7b792679323c6ca2ec633987ac6 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/f7/80903e237784afa195dc34276fd42d3ec1a7b792679323c6ca2ec633987ac6 new file mode 100644 index 0000000000..c90c532bcb --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/f7/80903e237784afa195dc34276fd42d3ec1a7b792679323c6ca2ec633987ac6 @@ -0,0 +1,19 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/f9/7e207ea3a551d55ffa0aa8fc59c9506a50d5ce4d61306c381080e26205791f b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/f9/7e207ea3a551d55ffa0aa8fc59c9506a50d5ce4d61306c381080e26205791f new file mode 100644 index 0000000000..e1b052c0ca --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/f9/7e207ea3a551d55ffa0aa8fc59c9506a50d5ce4d61306c381080e26205791f @@ -0,0 +1,25 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/f9/f59e2d7818d90669cb7eb06f864b13c8800c75e315006180d4cfd736757bb5 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/f9/f59e2d7818d90669cb7eb06f864b13c8800c75e315006180d4cfd736757bb5 new file mode 100644 index 0000000000..7699717f1e --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/f9/f59e2d7818d90669cb7eb06f864b13c8800c75e315006180d4cfd736757bb5 @@ -0,0 +1,21 @@ +I" + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/fa/9df514c0a07fba58476a5e04917db1fbe565e6e7307266a8b0830970a8beb1 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/fa/9df514c0a07fba58476a5e04917db1fbe565e6e7307266a8b0830970a8beb1 new file mode 100644 index 0000000000..5b9686d988 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/fa/9df514c0a07fba58476a5e04917db1fbe565e6e7307266a8b0830970a8beb1 @@ -0,0 +1,11 @@ +I"z + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/fb/de25c51b0454c9f2143282eb94b7c90eb37c27f06f1646ad16f5ea3fcba042 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/fb/de25c51b0454c9f2143282eb94b7c90eb37c27f06f1646ad16f5ea3fcba042 new file mode 100644 index 0000000000..e061a9a7fd --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/fb/de25c51b0454c9f2143282eb94b7c90eb37c27f06f1646ad16f5ea3fcba042 @@ -0,0 +1,5 @@ +I"E + +:ET \ No newline at end of file diff --git a/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/fc/868752ffc2cbf71f6b84e427bdfdbd7913312f446645bc11351b0cdfcdb7f5 b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/fc/868752ffc2cbf71f6b84e427bdfdbd7913312f446645bc11351b0cdfcdb7f5 new file mode 100644 index 0000000000..61d6be2de7 --- /dev/null +++ b/.jekyll-cache/Jekyll/Cache/Jekyll--Converters--Markdown/fc/868752ffc2cbf71f6b84e427bdfdbd7913312f446645bc11351b0cdfcdb7f5 @@ -0,0 +1,72 @@ +I"

bash脚本测试Ubuntu服务器的性能并输出报告

+ +

+#!/bin/bash
+
+# This script is used to test server performance and output report
+
+# Get the system information
+hostname=`hostname`
+kernel=`uname -r`
+
+# Get the CPU information
+cpu_model=`cat /proc/cpuinfo | grep "model name" | head -1 | cut -d: -f2`
+cpu_cores=`cat /proc/cpuinfo | grep "cpu cores" | head -1 | cut -d: -f2`
+cpu_freq=`cat /proc/cpuinfo | grep "cpu MHz" | head -1 | cut -d: -f2`
+
+# Get the memory information
+mem_total=`cat /proc/meminfo | grep "MemTotal" | cut -d: -f2`
+swap_total=`cat /proc/meminfo | grep "SwapTotal" | cut -d: -f2`
+
+# Get the disk information
+disk_total=`df -h | grep "/$" | awk '{print $2}'`
+disk_used=`df -h | grep "/$" | awk '{print $3}'`
+
+# Print the report
+echo "System Information"
+echo "-----------------"
+echo "Hostname: $hostname"
+echo "Kernel: $kernel"
+echo ""
+echo "CPU Information"
+echo "---------------"
+echo "Model: $cpu_model"
+echo "Cores: $cpu_cores"
+echo "Frequency: $cpu_freq MHz"
+echo ""
+echo "Memory Information"
+echo "-----------------"
+echo "Total: $mem_total"
+echo "Swap: $swap_total"
+echo ""
+echo "Disk Information"
+echo "----------------"
+echo "Total: $disk_total"
+echo "Used: $disk_used"
+echo ""
+
+ +

结果如下:

+
root@hugo-virtual-machine:/opt# sh service.sh 
+System Information
+-----------------
+Hostname: hugo-virtual-machine
+Kernel: 5.15.0-58-generic
+
+CPU Information
+---------------
+Model:  AMD Ryzen 7 4800H with Radeon Graphics
+Cores:  2
+Frequency:  2894.461 MHz
+
+Memory Information
+-----------------
+Total:         8105812 kB
+Swap:        1999868 kB
+
+Disk Information
+----------------
+Total: 23G
+Used: 12G
+
+:ET \ No newline at end of file diff --git a/Gemfile b/Gemfile index 0612ed55db..21390fcd83 100755 --- a/Gemfile +++ b/Gemfile @@ -3,3 +3,4 @@ gem 'jekyll' gem 'redcarpet' gem 'jekyll-paginate' gem 'jekyll-sitemap' + diff --git a/Gemfile.lock b/Gemfile.lock index afef6f8693..f5100620f1 100755 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,70 +1,78 @@ GEM remote: https://rubygems.org/ specs: - addressable (2.6.0) - public_suffix (>= 2.0.2, < 4.0) + addressable (2.8.0) + public_suffix (>= 2.0.2, < 5.0) colorator (1.1.0) - concurrent-ruby (1.1.4) - em-websocket (0.5.1) + concurrent-ruby (1.1.10) + em-websocket (0.5.3) eventmachine (>= 0.12.9) - http_parser.rb (~> 0.6.0) + http_parser.rb (~> 0) eventmachine (1.2.7) - ffi (1.10.0) + ffi (1.15.5-x64-mingw-ucrt) forwardable-extended (2.6.0) - http_parser.rb (0.6.0) - i18n (0.9.5) + http_parser.rb (0.8.0) + i18n (1.10.0) concurrent-ruby (~> 1.0) - jekyll (3.8.5) + jekyll (4.2.2) addressable (~> 2.4) colorator (~> 1.0) em-websocket (~> 0.5) - i18n (~> 0.7) - jekyll-sass-converter (~> 1.0) + i18n (~> 1.0) + jekyll-sass-converter (~> 2.0) jekyll-watch (~> 2.0) - kramdown (~> 1.14) + kramdown (~> 2.3) + kramdown-parser-gfm (~> 1.0) liquid (~> 4.0) - mercenary (~> 0.3.3) + mercenary (~> 0.4.0) pathutil (~> 0.9) - rouge (>= 1.7, < 4) + rouge (~> 3.0) safe_yaml (~> 1.0) + terminal-table (~> 2.0) jekyll-paginate (1.1.0) - jekyll-sass-converter (1.5.2) - sass (~> 3.4) - jekyll-sitemap (1.1.1) - jekyll (~> 3.3) - jekyll-watch (2.1.2) + jekyll-sass-converter (2.2.0) + sassc (> 2.0.1, < 3.0) + jekyll-sitemap (1.4.0) + jekyll (>= 3.7, < 5.0) + jekyll-watch (2.2.1) listen (~> 3.0) - kramdown (1.17.0) - liquid (4.0.1) - listen (3.1.5) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - ruby_dep (~> 1.2) - mercenary (0.3.6) + kramdown (2.4.0) + rexml + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + liquid (4.0.3) + listen (3.7.1) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + mercenary (0.4.0) pathutil (0.16.2) forwardable-extended (~> 2.6) - public_suffix (3.0.3) - rb-fsevent (0.10.3) - rb-inotify (0.10.0) + public_suffix (4.0.7) + rb-fsevent (0.11.1) + rb-inotify (0.10.1) ffi (~> 1.0) - redcarpet (3.4.0) - rouge (3.3.0) - ruby_dep (1.5.0) - safe_yaml (1.0.4) - sass (3.7.3) - sass-listen (~> 4.0.0) - sass-listen (4.0.0) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) + redcarpet (3.5.1) + rexml (3.2.5) + rouge (3.29.0) + safe_yaml (1.0.5) + sassc (2.4.0) + ffi (~> 1.9) + terminal-table (2.0.0) + unicode-display_width (~> 1.1, >= 1.1.1) + unicode-display_width (1.8.0) + wdm (0.1.1) + webrick (1.7.0) PLATFORMS - ruby + x64-mingw-ucrt DEPENDENCIES jekyll jekyll-paginate jekyll-sitemap redcarpet + wdm (>= 0.1.0) + webrick (~> 1.7) BUNDLED WITH - 1.16.1 + 2.3.7 diff --git a/README.md b/README.md index 2a9187d4c6..2069322903 100755 --- a/README.md +++ b/README.md @@ -1,115 +1 @@ - -[leopard](http://leopardpan.cn) 是一个简洁的博客模板,响应式主题, 适配了电脑、手机各种屏幕,看效果直接点击下面链接 - - * [博客链接](http://leopardpan.cn) (部署在国内,访问更快) - * [Demo链接](http://leopardpan.github.io/) (部署在github page) - -如果你喜欢请 Star ,你的 Star 是我持续更新的动力, 谢谢 😄. - -### 你在搭建个人博客遇到任何问题都可以找我 - -遇到解决不了的问题 [需要技术支持联系我](http://leopardpan.cn/support/) - - -### 环境要求 - -* Jekyll 支持: Mac 、Windows、ubuntu 、Linux 操作系统 -* Jekyll 需要依赖: Ruby、bundler - -### 使用手册 - -[Jekyll搭建个人博客](http://leopardpan.cn/2016/10/jekyll_tutorials1/) : 使用Jekyll搭建个人博客的教程,及如何把这个博客模板修改成你自己的博客,里面也有大量的评论、Jekyll 搭建博客各种环境出现过的问题。 - -[HEXO搭建个人博客](http://leopardpan.cn/2015/08/HEXO%E6%90%AD%E5%BB%BA%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2/) : 使用 HEXO 基于 Github Page 搭建个人博客, 教程里面累计了大量提问和评论,如果你在搭建博客时遇到问题,可以看看这个教程。 - - -#### 安装Jekyll - -[Jekyll中文官方文档](http://jekyll.bootcss.com/) , 如果你已经安装过了 Jekyll,可以忽略此处。 - -> $ gem install jekyll - -#### 获取博客模板 - -> $ git clone https://github.com/leopardpan/leopardpan.github.io.git - -或者直接[下载博客](https://github.com/leopardpan/leopardpan.github.io/archive/master.zip) - -进leopardpan.github.io/ 目录下, 开启本地服务 - -> $ jekyll server - -在浏览器输入 [127.0.0.1:4000](127.0.0.1:4000) , 就可以看到博客效果了。 - - -### 提示 - ->* 如果你想使用我的模板,请把 _posts/ 目录下的文章都去掉。 ->* 修改 _config.yml 文件里面的内容为你自己的个人信息。 - -如果在部署博客的时候发现问题,可以直接在[Issues](https://github.com/leopardpan/leopardpan.github.io/issues)里面提问。 - - -### 把这个博客变成你自己的博客 - -根据上面【提示】修改过后,在你的github里创建一个username.github.io的仓库,username指的值你的github的用户名。 -创建完成后,把我的这个模板使用git push到你的username.github.io仓库下就行了。 -搭建博客如果遇到问题可以看看我教程[Jekyll搭建个人博客](http://leopardpan.cn/2016/10/jekyll_tutorials1/)。 - - -### 赞助 - -你可以通过下方二维码赞助本项目,资金将用于服务器开销以及今后的公共服务 - -感谢所有赞助过本项目的朋友,你们都为本项目贡献了自己的一份力量 - -
- -微信二维码 -wechat -
- -
- -支付宝二维码 -alipay -
- - -### 效果预览 - -#### 头像效果 - -![](http://leopardpan.github.io/images/readme/icon.gif) - -如果你只想要我博客里的头像效果,你只需要拿 leopardpan.github.io/_includes/side-panel.html 文件里面 `头像效果` 和 leopardpan.github.io/css/main.css 里面最后面 `头像效果` 部分就行了。 - - -*** - -#### 博客首页 - -![](http://leopardpan.github.io//images/readme/img4.png) - -*** - -#### 每篇文章下面都支持打赏 - -![](http://leopardpan.github.io/images/readme/img3.png) - -#### 文章详情 - -![](http://leopardpan.github.io/images/readme/img1.png) - - -#### 文章支持标签分类 - -![](http://leopardpan.github.io/images/readme/img2.png) - -#### 手机端效果 - -wechat - -#### 感谢 - -本博客在[Vno Jekyll](https://github.com/onevcat/vno-jekyll)基础上修改的。 \ No newline at end of file +## 物理海洋学在读 diff --git a/_config.yml b/_config.yml index 9795797653..c40bd3b830 100755 --- a/_config.yml +++ b/_config.yml @@ -1,50 +1,49 @@ # Basic -title: 潘柏信 +title: 陈同学 subtitle: 个人站 description: 欢迎来到我的个人站~ # 头像里面的标题 -avatarTitle: leopardpan +avatarTitle: nothing can stop me # 头像里面的描述 -avatarDesc: iOS / 机器学习 -url: "http://leopardpan.cn" +avatarDesc: 物理海洋学在读 +url: "https://chanjeunlam.github.io/" # 博客是否自动生成目录,false为不生成 enableToc: true # Comment -comment: - # livere: MTAyMC81MDk5NC8yNzQ3Ng # https://www.livere.com - disqus: leopard - # uyan: 2143225 # 有言id,登录有言官网申请 +# comment: +# # livere: MTAyMC81MDk5NC8yNzQ3Ng # https://www.livere.com +# disqus: leopard +# # uyan: 2143225 # 有言id,登录有言官网申请 # Social social: - weibo: 5366874726 - github: leopardpan + # weibo: + # github: # zhihu: panpanleopard # juejin: xxx - jianshu: 2ada30d8d0f7 + # jianshu: 2ada30d8d0f7 # twitter: - mail: leopardpan@icloud.com + mail: jchenfl@connect.ust.hk # 百度统计 # 使用博客模板请去掉或者更换 id -baidu: - id: 8ba1c6be0953b6f9c2ba38e57f615421 +# baidu: +# id: 8ba1c6be0953b6f9c2ba38e57f615421 # Google Analytics # 使用博客模板请去掉或者更换 id -ga: - id: UA-84134159-3 - host: auto +# ga: +# id: UA-84134159-3 +# host: auto # 《----------------- 上面的信息修改成你自己的 ---------------------------》 # # # _post/ 下的文章更换成你自己的文章 -# 帮忙star一下,感谢~😆 地址: https://github.com/leopardpan/leopardpan.github.io # # # 《--------------- 下面的信息可以自行选择是否修改 ------------------------》 @@ -73,7 +72,16 @@ nav: # Pagination gems: [jekyll-paginate,jekyll-sitemap] +plugins: [jekyll-paginate] paginate: 20 paginate_path: "page/:num/" - +# fomula +markdown: kramdown +kramdown: + math_engine: mathjax + syntax_highlighter: rouge + input: GFM + hard_wrap: false + syntax_highlighter_opts: + disable: true \ No newline at end of file diff --git a/_includes/open-embed.html b/_includes/open-embed.html new file mode 100644 index 0000000000..e45e8d553c --- /dev/null +++ b/_includes/open-embed.html @@ -0,0 +1,95 @@ + + + diff --git a/_layouts/default.html b/_layouts/default.html index e21abb61fd..d4323a978f 100755 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -32,8 +32,12 @@ {% include footer.html %} - + + {% include external.html %} + {% include open-embed.html %} + + diff --git a/_layouts/post.html b/_layouts/post.html index 71d4530a7e..2c8f35b99f 100755 --- a/_layouts/post.html +++ b/_layouts/post.html @@ -4,6 +4,22 @@ + + + + + + + +
diff --git "a/_posts/2015-08-25-HEXO\346\220\255\345\273\272\344\270\252\344\272\272\345\215\232\345\256\242.md" "b/_posts/2015-08-25-HEXO\346\220\255\345\273\272\344\270\252\344\272\272\345\215\232\345\256\242.md" deleted file mode 100755 index f14503f0ba..0000000000 --- "a/_posts/2015-08-25-HEXO\346\220\255\345\273\272\344\270\252\344\272\272\345\215\232\345\256\242.md" +++ /dev/null @@ -1,200 +0,0 @@ ---- -layout: post -title: "HEXO搭建个人博客" -date: 2015-08-25 -description: "HEXO配置,HEXO+Github,搭建自己的博客" -tag: hexo ---- - -  经过各种找资料,踩过各种坑,终于使用 hexo 搭建个人博客初步完成了,域名目前用得时 github 的,我的 hexo 是 3.1.1 版本,hexo 不同的版本,很多配置都不一样。好吧,废话不多说了,开始吧。 - -  本篇文章累计了大量评论,和我的一些解答,读者有什么问题可以先看看评论,或者搜下关键字,如果还是有问题可以给我在评论里给我留言,问题很着急的可以加我 QQ:1499065178 ,或者给我发邮件:[leopardpan@icloud.com](mailto:leopardpan@icloud.com) ,都可以,如果你已经搭建完成了,可以看看我的另一篇博客[HEXO进阶](http://leopardpan.cn/2016/06/HEXO_Advanced/),有讲如何能百度搜索你的博客、多台电脑使用HEXO发布博客等。我现在的博客是使用 Jekyll 搭建的,如果你想使用我现在的模板请看[Jekyll搭建个人博客](http://leopardpan.cn/2016/10/jekyll_tutorials1/) - - -## 背景 - - 这篇教程是针对与Mac的,[参考链接](http://ibruce.info/2013/11/22/hexo-your-blog/?utm_source=tuicool),由于原文讲到的hexo是以前的老版本,所以现在的版本配置的时候会有些改动。 - - 之前是想着写博客,一方面是给自己做笔记,可以提升自己的写作、总结能力,一个技术点我们会使用,并不难,但是要做到让让别人也能听懂我们讲得,还是需要一定的技巧和经验的。很多类似于CSDN、博客园也都可以写文章,但是页面的样式我,不是太喜欢,简书还算好点得。最近看到一些大神们的博客(在我的友情链接里有),貌似都是用hexo写得,我也依葫芦画瓢的搭建了一个。不罗嗦了,直接上搭建步骤。 - -## 配置环境 - -### 安装Node(必须) - -作用:用来生成静态页面的 -到Node.js[官网](https://nodejs.org/)下载相应平台的最新版本,一路安装即可。 - -### 安装Git(必须) -作用:把本地的hexo内容提交到github上去. -安装Xcode就自带有Git,我就不多说了。 - -### 申请GitHub(必须) -作用:是用来做博客的远程创库、域名、服务器之类的,怎么与本地hexo建立连接等下讲。 -[github](https://github.com/)账号我也不再啰嗦了,没有的话直接申请就行了,跟一般的注册账号差不多,SSH Keys,看你自己了,可以不配制,不配置的话以后每次对自己的博客有改动提交的时候就要手动输入账号密码,配置了就不需要了,怎么配置我就不多说了,网上有很多教程。 - -### 正式安装HEXO  -Node和Git都安装好后,可执行如下命令安装hexo: - - $ sudo npm install -g hexo - -### 初始化 -创建一个文件夹,如:Blog,cd到Blog里执行hexo init的。命令: - - - hexo init - - -好啦,至此,全部安装工作已经完成! - -### 生成静态页面 -继续再Blog目录下执行如下命令,生成静态页面 - - hexo generate (hexo g 也可以) - -### 本地启动 -启动本地服务,进行文章预览调试,命令: - - hexo server - -浏览器输入[http://localhost:4000](就可以看到最原始的效果了) -我不知道你们能不能,反正我不能,因为我还有环境没配置好 - -### 常见的HEXO配置错误: - -``` -ERROR Plugin load failed: hexo-server - -原因: Besides, utilities are separated into a standalone module. hexo.util is not reachable anymore. - -解决方法,执行命令:$ sudo npm install hexo-server - -``` - -``` -执行命令hexo server,提示:Usage: hexo .... - -原因:我认为是没有生成本地服务 - -解决方法,执行命令:$ npm install hexo-server --save - -提示:hexo-server@0.1.2 node_modules/hexo-server -.... - -表示成功了[参考](https://hexo.io/zh-cn/docs/server.html) - -``` - -这个时候再执行:$ hexo-server - -得到: INFO Hexo is running at http://0.0.0.0:4000/. Press Ctrl+C to stop. - -这个时候再点击[http://0.0.0.0:4000](http://0.0.0.0:4000),正常情况下应该是最原始的画面,但是我看到的是: -白板和Cannot GET / 几个字 -原因: 由于2.6以后就更新了,我们需要手动配置些东西,我们需要输入下面三行命令: - - npm install hexo-renderer-ejs --save - npm install hexo-renderer-stylus --save - npm install hexo-renderer-marked --save - -这个时候再重新生成静态文件,命令: - - hexo generate (或hexo g) - -启动本地服务器: - - hexo server (或hexo s) - -再点击网址[http://0.0.0.0:4000](http://0.0.0.0:4000) OK终于可以看到属于你自己的blog啦,?,虽然很简陋,但好歹有了一个属于自己的小窝了。[参考链接](https://disqus.com/home/discussion/bsspirit/hexogithubweb/),本地已经简单的设置好了,但是现在域名和服务器都是基于自己的电脑,接下来需要跟github进行关联。 - - -## 配置Github - -### 建立Repository - -建立与你用户名对应的仓库,仓库名必须为【your_user_name.github.io】,固定写法 -然后建立关联,我的Blog在本地/Users/leopard/Blog,Blog是我之前建的东西也全在这里面,有: - -> Blog ->  | ->  |-- _config.yml ->  |-- node_modules ->  |-- public ->  |-- source ->  |-- db.json ->  |-- package.json ->  |-- scaffolds ->  |-- themes       -    - -现在我们需要_config.yml文件,来建立关联,命令: - - vim _config.yml - -翻到最下面,改成我这样子的,注意: `: 后面要有空格` - - deploy: - type: git - repository: https://github.com/leopardpan/leopardpan.github.io.git - branch: master - -执行如下命令才能使用git部署 - - npm install hexo-deployer-git --save - -网上会有很多说法,有的type是github, 还有repository 最后面的后缀也不一样,是github.com.git,我也踩了很多坑,我现在的版本是hexo: 3.1.1,执行命令hexo -vsersion就出来了,貌似3.0后全部改成我上面这种格式了。 -忘了说了,我没用SSH Keys如果你用了SSH Keys的话直接在github里复制SSH的就行了,总共就两种协议,相信你懂的。 -然后,执行配置命令: - - hexo deploy - - 然后再浏览器中输入[http://leopardpan.github.io/](http://leopardpan.github.io/)就行了,我的 github 的账户叫 leopardpan ,把这个改成你 github 的账户名就行了 - -### 部署步骤 - -每次部署的步骤,可按以下三步来进行。 - - hexo clean - hexo generate - hexo deploy - -一些常用命令: - - hexo new "postName" #新建文章 - hexo new page "pageName" #新建页面 - hexo generate #生成静态页面至public目录 - hexo server #开启预览访问端口(默认端口4000,'ctrl + c'关闭server) - hexo deploy #将.deploy目录部署到GitHub - hexo help #查看帮助 - hexo version #查看Hexo的版本 - -这里有大量的[主题列表](https://github.com/hexojs/hexo/wiki/Themes)使用方法里面 -都有详细的介绍,我就不多说了。 -我这里有几款个人认为不错的主题,免去你们,一个一个的选了,欢迎吐槽我的审美,? - [Cover](https://github.com/daisygao/hexo-themes-cover) - A chic theme with facebook-like cover photo - [Oishi](https://github.com/henryhuang/oishi) - A white theme based on Landscape plus and Writing. - [Sidebar](https://github.com/hardywu/hexo-theme-sidebar) - Another theme based on Light with a simple sidebar - [TKL](https://github.com/SuperKieran/TKL) - A responsive design theme for Hexo. 一个设计优雅的响应式主题 - [Tinnypp](https://github.com/levonlin/Tinnypp) - A clean, simple theme based on Tinny - [Writing](https://github.com/yunlzheng/hexo-themes-writing) - A small and simple hexo theme based on Light - [Yilia](https://github.com/litten/hexo-theme-yilia) - Responsive and simple style 优雅简洁响应式主题,我用得就是这个。 - [Pacman voidy](https://github.com/Voidly/pacman) - A theme with dynamic tagcloud and dynamic snow - -## 博客目录介绍 - 文章在 source/_posts,编辑器可以用 Sublime,支持 markdown 语法。如果想修改头像可以直接在主题的 _config.yml 文件里面修改,友情链接,之类的都在这里,修改名字在 public/index.html 里修改,开始打理你的博客吧,有什么问题或者建议,都可以提出来,我会继续完善的。 - -Markdown语法参考链接: [作业部落](https://www.zybuluo.com/mdeditor) - - -## Q&A - -> 问:如何让文章想只显示一部分和一个 `阅读全文` 的按钮? -> 答:在文章中加一个 `` , `` 后面的内容就不会显示出来了。 - -

- -> 问:本地部署成功了,也能预览效果,但使用 username.github.io 访问,`出现 404 ` . -> 答:首先确认 hexo d 命令执行是否报错,如果没有报错,再查看一下你的 github 的 username.github.io 仓库,你的博客是否已经成功提交了,你的 github 邮箱也要通过验证才行。 - -

- -转载请注明原地址,潘柏信的博客:[http://leopardpan.github.io](http://leopardpan.github.io) 谢谢! diff --git a/_posts/2016-06-12-HEXO_Advanced.md b/_posts/2016-06-12-HEXO_Advanced.md deleted file mode 100755 index 4cfd755714..0000000000 --- a/_posts/2016-06-12-HEXO_Advanced.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -layout: post -title: HEXO进阶 -tag: hexo ---- - -HEXO接近是最近有一些朋友提出的问题,然后我做了总结,如果你也在使用HEXO,不妨看看,应该会有些帮助。 - -* 1、博客部署样式出问题了怎么办? -* 2、电脑重装或者误删了本地博客怎么办? -* 3、想使用两台电脑写博客怎么办? -* 4、为何使用百度搜不到我的博客? - - -### 使用Jekyll解决前三个问题。 -不得不说 `Jekyll` 确实可以解决我上面三个问题, 因为 `Jekyll` 是直接把Markdown格式的文章直接放在github仓库里的, 相当于直接用git来管理博客了, `Github` 官方也很推荐 `Jekyll` 。 你可以先看下 `Jekyll` 搭建博客的[voyagelab](voyagelab.github.io), [github地址](https://github.com/voyagelab/voyagelab.github.io), 当然了这只是很普通的, Jekyll 也有很多主题可以选择的, 更详细的请看[Jekyll中文文档](http://jekyll.bootcss.com/)、[Jekyll英文文档](https://jekyllrb.com/)、[Jekyll主题列表](http://jekyllthemes.org/)。 -在 `Jekyll` 上逛了一个星期的我又回到了 `Hexo` , 发现 目前 `Jekyll` 对主题和一些插件的支持相对 `Hexo` 来说, 没那么友好, 可能有一些其它的方法只是我没找到而已,关于 `Jekyll` 搭建博客就介绍到这, 如果有问题的话可以评论, 或者联系我。 - -### 使用Hexo解决上面前三个问题 -是的, 我大`Hexo`同样可以解决上面三个问题, 那就是使用git。关于如何使用 `Hexo` 搭建博客请看我另一篇文章[HEXO搭建个人博客](http://www.leopardpan.cn/2015/08/25/HEXO%E6%90%AD%E5%BB%BA%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2/), 如果搭建的过程中出现了问题, 我们可以交流交流。现在我假设你已经能基本使用 `Hexo` 了, 接下来就看看如何来管理博客。 - -## 使用git管理博客 -[Blog-Growing](https://github.com/leopardpan/Blog-Growing)是我管理博客的仓库地址。结构大致是: - -> -- Blog-Growing ->   |-- .git ->   |-- .gitignore ->   |-- Hexo ->     | .. ->     | .. ->     | 整个博客的配置信息 - -你可以把我的的[Blog-Growing](https://github.com/leopardpan/Blog-Growing) clone下来, 看看一些基本配置是怎么配置的。需要提醒的是, `要把主题下的.git给删除掉` , 当然了你也可以使用 `git submodule` 来管理, 具体的管理方法我这里就不做多的介绍了, 想了解的话可以私信我。 -上面的过程基本做完后, 怎么来解决我开篇提的前三个问题了。 - -### 具体实现: -**一:家里电脑使用博客** -  建立git远端仓库管理博客,并使用家里的电脑把本地博客的配置推送到远端仓库。 -**二:公司电脑使用博客** -  到了公司只需要执行`sudo npm install -g hexo`,然后cd到你的博客目录下,如我cd 到Hexo目录下, 然后执行 `hexo server` 就可以在本地预览博客了。 -**三:使用Git保存** -  修改好博客后记得先使用git来提交下, 即使下次把博客的样式修改坏了, 也可以使用 `git reset --hard` 来回退。如: 我cd 到 `Blog-Growing` 目录下使用git提交。 -**四:博客提交** -  1、修改好的博客使用 `hexo d` 展示到博客页上。 -  2、git push 整个本地博客。 - -**提示:** 在这里 `git` 仅仅只是用户做博客的版本管理的, 博客的样式修改、基本部署还是使用 `hexo` 来操作的。 - -## 让百度能搜索到你的博客 - -### 为什么要使用百度搜索? - -  有人可能会说作为一个开发人员, 你不会用 `Google` 啊。 是的, Google是能搜到我们搭建在 `Github Page` 的博客, 会用`Google` 也是一个开发人员必备技能之一。但是, 我们生活在天朝, 所以百度还是总有会用到的时候, 或者是你想让更多的天朝人能搜到你。 - -### 为什么使用百度搜索不到 Github Page 上的博客? - -有人联系过 Github Support 部门 , 给出大致的意思就是: 百度爬虫爬得太猛烈,已经对很多 Github 用户造成了问题。所以 Github 将禁止百度爬虫的爬取。 - -### 如何让百度能搜索你的博客? - -  根据上面说的, 目前发现只是Github Page禁止了百度搜索, 所以让百度能搜索到你的博客还是有一些方法的。例如: -* 自己搞个VPS,博客部署在VPS上。 -* 博客部署 `Coding.net` 上, `GitCafe`已经合并到 `Coding` 。 -我使用的是第二种方法, 博客部署在 `Coding.net` 上也相对简单些。 - -#### 在Coding上部署你的博客。 - -  Coding同样支持Hexo、Jekyll等博客的部署, Coding 跟Github还是挺像的,而且是中文。 同样的在Coding里面建一个项目,项目名字跟你的用户名一样,这里我就不啰嗦了, 说几个需要注意的地方: -**注意一:** -  在`Coding Page` 上部署博客,需要把博客推送到`coding-pages ` 分支上, 分支名字是固定的。 -**注意二:** -  `Coding Page` 不支持自定义CNAME, 你需要点击到Page模块,然后添加一个域名来绑定。 - -更详细的请看[Coding Pages 官网介绍](https://coding.net/help/doc/pages/index.html). - -参考文章: -[解决 Github Pages 禁止百度爬虫的方法与可行性分析](http://jerryzou.com/posts/feasibility-of-allowing-baiduSpider-for-Github-Pages/) - -
- -转载请注明:[潘柏信的博客](http://leopardpan.cn) » [点击阅读原文](http://leopardpan.cn/2016/06/HEXO_Advanced/) \ No newline at end of file diff --git a/_posts/2016-10-14-jekyll_tutorials1.md b/_posts/2016-10-14-jekyll_tutorials1.md deleted file mode 100755 index aa913b1545..0000000000 --- a/_posts/2016-10-14-jekyll_tutorials1.md +++ /dev/null @@ -1,291 +0,0 @@ ---- -layout: post -title: Jekyll搭建个人博客 -date: 2016-10-14 -tags: jekyll ---- - - 之前写了一篇[HEXO搭建个人博客](http://leopardpan.cn/2015/08/HEXO%E6%90%AD%E5%BB%BA%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2/)的教程获得了很好评,尤其是在[简书](http://www.jianshu.com/p/465830080ea9)上目前已经累积了10W+的阅读量了,也有好心的读者主动给我打赏,在此感谢。 - - 如果你看过我的文章会发现我现在的博客样式跟之前是有很大的区别的,之前我也是使用 HEXO 搭建的博客,后来发现使用 HEXO 在多台电脑上发布博客,操作起来并不是那么方便,果断就转到了 Jekyll 上,接下来我会讲如何使用 Jekyll 搭建博客,[博客模板效果](http://leopardpan.cn/#blog)。 - - -### 介绍 - -  Jekyll 是一个简单的博客形态的静态站点生产机器。它有一个模版目录,其中包含原始文本格式的文档,通过 Markdown (或者 Textile) 以及 Liquid 转化成一个完整的可发布的静态网站,你可以发布在任何你喜爱的服务器上。Jekyll 也可以运行在 GitHub Page 上,也就是说,你可以使用 GitHub 的服务来搭建你的项目页面、博客或者网站,而且是完全免费的 - - 使用 Jekyll 搭建博客之前要确认下本机环境,Git 环境(用于部署到远端)、[Ruby](http://www.ruby-lang.org/en/downloads/) 环境(Jekyll 是基于 Ruby 开发的)、包管理器 [RubyGems](http://rubygems.org/pages/download) -  如果你是 Mac 用户,你就需要安装 Xcode 和 Command-Line Tools了。下载方式 Preferences → Downloads → Components。 - -  Jekyll 是一个免费的简单静态网页生成工具,可以配合第三方服务例如: Disqus(评论)、多说(评论) 以及分享 等等扩展功能,Jekyll 可以直接部署在 Github(国外) 或 Coding(国内) 上,可以绑定自己的域名。[Jekyll中文文档](http://jekyll.bootcss.com/)、[Jekyll英文文档](https://jekyllrb.com/)、[Jekyll主题列表](http://jekyllthemes.org/)。 - - -### Jekyll 环境配置 - -安装 jekyll - -``` -$ gem install jekyll -``` - -创建博客 - -``` -$ jekyll new myBlog -``` - -进入博客目录 - -``` -$ cd myBlog -``` - -启动本地服务 - -``` -$ jekyll serve -``` - -在浏览器里输入: [http://localhost:4000](http://localhost:4000),就可以看到你的博客效果了。 - -![](/images/posts/jekyll/image1.png) - -so easy ! - -### 目录结构 -  - Jekyll 的核心其实是一个文本转换引擎。它的概念其实就是: 你用你最喜欢的标记语言来写文章,可以是 Markdown,也可以是 Textile,或者就是简单的 HTML, 然后 Jekyll 就会帮你套入一个或一系列的布局中。在整个过程中你可以设置URL路径, 你的文本在布局中的显示样式等等。这些都可以通过纯文本编辑来实现,最终生成的静态页面就是你的成品了。 - - 一个基本的 Jekyll 网站的目录结构一般是像这样的: - -``` -. -├── _config.yml -├── _includes -| ├── footer.html -| └── header.html -├── _layouts -| ├── default.html -| ├── post.html -| └── page.html -├── _posts -| └── 2016-10-08-welcome-to-jekyll.markdown -├── _sass -| ├── _base.scss -| ├── _layout.scss -| └── _syntax-highlighting.scss -├── about.md -├── css -| └── main.scss -├── feed.xml -└── index.html - -``` - -这些目录结构以及具体的作用可以参考 [官网文档](http://jekyll.com.cn/docs/structure/) - -进入 _config.yml 里面,修改成你想看到的信息,重新 jekyll server ,刷新浏览器就可以看到你刚刚修改的信息了。 - -到此,博客初步搭建算是完成了, - -### 博客部署到远端 - - 我这里讲的是部署到 Github Page 创建一个 github 账号,然后创建一个跟你账户名一样的仓库,如我的 github 账户名叫 [leopardpan](https://github.com/leopardpan),我的 github 仓库名就叫 [leopardpan.github.io](https://github.com/leopardpan/leopardpan.github.io),创建好了之后,把刚才建立的 myBlog 项目 push 到 username.github.io仓库里去(username指的是你的github用户名),检查你远端仓库已经跟你本地 myBlog 同步了,然后你在浏览器里输入 username.github.io ,就可以访问你的博客了。 - - -### 编写文章 - -  所有的文章都是 _posts 目录下面,文章格式为 mardown 格式,文章文件名可以是 .mardown 或者 .md。 - -  编写一篇新文章很简单,你可以直接从 _posts/ 目录下复制一份出来 `2016-10-16-welcome-to-jekyll副本.markdown` ,修改名字为 2016-10-16-article1.markdown ,注意:文章名的格式前面必须为 2016-10-16- ,日期可以修改,但必须为 年-月-日- 格式,后面的 article1 是整个文章的连接 URL,如果文章名为中文,那么文章的连接URL就会变成这样的:http://leopardpan.cn/2015/08/%E6%90%AD%E5/ , 所以建议文章名最好是英文的或者阿拉伯数字。 双击 2016-10-16-article1.markdown 打开 - -``` - ---- -layout: post -title: "Welcome to Jekyll!" -date: 2016-10-16 11:29:08 +0800 -categories: jekyll update ---- - -正文... - -``` - - -title: 显示的文章名, 如:title: 我的第一篇文章 -date: 显示的文章发布日期,如:date: 2016-10-16 -categories: tag标签的分类,如:categories: 随笔 - -注意:文章头部格式必须为上面的,.... 就是文章的正文内容。 - -我写文章使用的是 Sublime Text2 编辑器,如果你对 markdown 语法不熟悉的话,可以看看[作业部落的教程](https://www.zybuluo.com/) - - -### 使用我的博客模板 - -虽然博客部署完成了,你会发现博客太简单不是你想要的,如果你喜欢我的模板的话,可以使用我的模板。 - -首先你要获取的我博客,[Github项目地址](https://github.com/leopardpan/leopardpan.github.io.git),你可以直接[点击下载博客](https://github.com/leopardpan/leopardpan.github.io/archive/master.zip),进去leopardpan.github.io/ 目录下, 使用命令部署本地服务 - -``` -$ jekyll server -``` - -### 如果你本机没配置过任何jekyll的环境,可能会报错 - -``` -/Users/xxxxxxxx/.rvm/rubies/ruby-2.2.2/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require': cannot load such file -- bundler (LoadError) - from /Users/xxxxxxxx/.rvm/rubies/ruby-2.2.2/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require' - from /Users/xxxxxxxx/.rvm/gems/ruby-2.2.2/gems/jekyll-3.3.0/lib/jekyll/plugin_manager.rb:34:in `require_from_bundler' - from /Users/xxxxxxxx/.rvm/gems/ruby-2.2.2/gems/jekyll-3.3.0/exe/jekyll:9:in `' - from /Users/xxxxxxxx/.rvm/gems/ruby-2.2.2/bin/jekyll:23:in `load' - from /Users/xxxxxxxx/.rvm/gems/ruby-2.2.2/bin/jekyll:23:in `
' - from /Users/xxxxxxxx/.rvm/gems/ruby-2.2.2/bin/ruby_executable_hooks:15:in `eval' - from /Users/xxxxxxxx/.rvm/gems/ruby-2.2.2/bin/ruby_executable_hooks:15:in `
' - -``` - -原因: 没有安装 bundler ,执行安装 bundler 命令 - -``` - -$ gem install bundler - -``` - - -提示: - -``` -Fetching: bundler-1.13.5.gem (100%) -Successfully installed bundler-1.13.5 -Parsing documentation for bundler-1.13.5 -Installing ri documentation for bundler-1.13.5 -Done installing documentation for bundler after 5 seconds -1 gem installed - -``` - -再次执行 $ jekyll server ,提示 - -``` - -Could not find proper version of jekyll (3.1.1) in any of the sources -Run `bundle install` to install missing gems. - -``` - -跟着提示运行命令 - -``` -$ bundle install -``` - -这个时候你可能会发现 bundle install 运行卡主不动了。 - -如果很长时间都没任何提示的话,你可以尝试修改 gem 的 source - -``` -$ gem sources --remove https://rubygems.org/ -$ gem sources -a http://ruby.taobao.org/ -$ gem sources -l -*** CURRENT SOURCES *** - -http://ruby.taobao.org - -``` - -再次执行命令 $ bundle install,发现开始有动静了 - -``` -Fetching gem metadata from https://rubygems.org/........... -Fetching version metadata from https://rubygems.org/.. -Fetching dependency metadata from https://rubygems.org/. -。。。 -Installing jekyll-watch 1.3.1 -Installing jekyll 3.1.1 -Bundle complete! 3 Gemfile dependencies, 17 gems now installed. -Use `bundle show [gemname]` to see where a bundled gem is installed. - -``` - -bundler安装完成,后再次启动本地服务 - -``` -$ jekyll server - -``` - -继续报错 - -``` -Configuration file: /Users/tendcloud-Caroline/Desktop/leopardpan.github.io/_config.yml - Dependency Error: Yikes! It looks like you don't have jekyll-sitemap or one of its dependencies installed. In order to use Jekyll as currently configured, you'll need to install this gem. The full error message from Ruby is: 'cannot load such file -- jekyll-sitemap' If you run into trouble, you can find helpful resources at http://jekyllrb.com/help/! -jekyll 3.1.1 | Error: jekyll-sitemap - -``` -表示 当前的 jekyll 版本是 3.1.1 ,无法使用 jekyll-sitemap - -解决方法有两个 - -> 1、打开当前目录下的 _config.yml 文件,把 gems: [jekyll-paginate,jekyll-sitemap] 换成 gems: [jekyll-paginate] ,也就是去掉jekyll-sitemap。 - -> 2、升级 jekyll 版本,我当前的是 jekyll 3.1.2 。 - -修改完成后保存配置,再次执行 - -``` -$ jekyll server - -``` -提示 - -``` -Configuration file: /Users/baixinpan/Desktop/OpenSource/Mine/Page-Blog/leopardpan.github.io-github/_config.yml - Source: /Users/baixinpan/Desktop/OpenSource/Mine/Page-Blog/leopardpan.github.io-github - Destination: /Users/baixinpan/Desktop/OpenSource/Mine/Page-Blog/leopardpan.github.io-github/_site - Incremental build: disabled. Enable with --incremental - Generating... - done in 0.901 seconds. - Auto-regeneration: enabled for '/Users/baixinpan/Desktop/OpenSource/Mine/Page-Blog/leopardpan.github.io-github' -Configuration file: /Users/baixinpan/Desktop/OpenSource/Mine/Page-Blog/leopardpan.github.io-github/_config.yml - Server address: http://127.0.0.1:4000/ - Server running... press ctrl-c to stop. - -``` - -表示本地服务部署成功。 - -在浏览器输入 [127.0.0.1:4000](127.0.0.1:4000) , 就可以看到[leopardpan.cn](http://leopardpan.cn)博客效果了。 - -### 修改成你自己的博客 - ->* 如果你想使用我的模板请把 _posts/ 目录下的文章都去掉。 ->* 修改 _config.yml 文件里面的内容为你自己的。 - -然后使用 git push 到你自己的仓库里面去,检查你远端仓库,在浏览器输入 username.github.io 就会发现,你有一个漂亮的主题模板了。 - - -### 为什么要是用 Jekyll - -使用了 Jekyll 你会发现如果你想使用多台电脑发博客都很方便,只要把远端 github 仓库里的博客 clone 下来,写文章后再提交就可以了,Hexo 由于远端提交的是静态网页,所有无法直接写 Markdown 的文章。如果你想看 Hexo 搭建博客,可以看看我的另一篇[HEXO搭建个人博客](http://leopardpan.cn/2015/08/HEXO%E6%90%AD%E5%BB%BA%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2/)的教程。 - -如果你在搭建博客遇到问题,可以在[原文博客](http://leopardpan.cn/2016/10/jekyll_tutorials1/)的评论里给我提问。 - -后面会继续介绍,在我的博客基础上,如何修改成你自己喜欢的 Style,欢迎继续关注我博客的更新。 - - -### Q&A - -> 问题:最近很多朋友使用我的模板报警告:The CNAME `leopardpan.cn` is already taken -> 解决:把CNAME里面的baixin.io修改成你自己的域名,如果你暂时没有域名,CNAME里面就什么都不用谢。(之前没人反馈过这个问题,应该是github page最近才最的限制。) - - - - - - - - - diff --git a/_posts/2016-11-20-markdownTool.md b/_posts/2016-11-20-markdownTool.md deleted file mode 100755 index be05a3cbdb..0000000000 --- a/_posts/2016-11-20-markdownTool.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -layout: post -title: 文档支持的Markdown语法 -date: 2016-11-20 -tags: markdown ---- - - -## 什么是 Markdown - -Markdown 是一种方便记忆、书写的纯文本标记语言,用户可以使用这些标记符号以最小的输入代价生成极富表现力的文档:如您正在阅读的这篇文章。它使用简单的符号标记不同的标题,分割不同的段落,**粗体** 或者 *斜体* 某些文字. - -很多产品的文档也是用markdown编写的,并且以“README.MD”的文件名保存在软件的目录下面。 -   -## 一些基本语法 - -标题 -H1 :# Header 1 -H2 :## Header 2 -H3 :### Header 3 -H4 :#### Header 4 -H5 :##### Header 5 -H6 :###### Header 6 -链接 :[Title](URL) -加粗 :**Bold** -斜体字 :*Italics* -*删除线 :~~text~~ -内嵌代码 : `alert('Hello World');` - -### 列表 - -* 列表1 -* 列表2 -* 列表3 - -### 列表引用 - ->* 列表1 ->* 列表2 ->* 列表3 - -### 插入一张图片 - -打赏一个吧 - -![](/images/payimg/weipayimg.jpg) - -css 的大部分语法同样可以在 markdown 上使用,但不同的渲染器渲染出来的 markdown 内容样式也不一样,下面这些链接里面有 markdown 基本语法,你也可以在下面几个平台上尝试着写一些。 - -## 博客支持的高级语法 - -### 1. 制作一份待办事宜 - -- [ ] 支持以 PDF 格式导出文稿 -- [ ] 改进 Cmd 渲染算法,使用局部渲染技术提高渲染效率 -- [x] 新增 Todo 列表功能 -- [x] 修复 图标功能 - -### 2. 高亮一段代码 - -```python - -@requires_authorization -class SomeClass: - pass - -if __name__ == '__main__': - # A comment - print 'hello world' - -``` - -#### 3. 绘制表格 - -| 项目 | 价格 | 数量 | -| -------- | -----: | :----: | -| 计算机 | \$1600 | 5 | -| 手机 | \$12 | 12 | -| 管线 | \$1 | 234 | - - diff --git a/_posts/2020-07-10-ReleaseNode.md b/_posts/2020-07-10-ReleaseNode.md deleted file mode 100755 index 2f94587023..0000000000 --- a/_posts/2020-07-10-ReleaseNode.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -layout: post -title: 博客迭代记录 -date: 2020-07-10 -tags: jekyll ---- - -## 介绍 - -本博客首发时间为2015年,至今已经5个年头了。 - -感觉之前更新迭代的有点乱,很多用户在使用的时候也没法确定是否要更新新版的博客,再次我专门用一篇文章来记录博客更新迭代的记录,如果你已经在使用我的博客模板的,你可以看看更新记录后确认下是否要更新。 - -博客的全功能介绍请看[博客功能介绍](http://leopardpan.cn/2020/07/blog_info/) - -## 1.0.0 - -2016-11-25 第一版发布,红色主题 - -## 2.0.0 开发中 - -* 2016-11-25 修改博客风格为淡蓝色主题 -* 2016-12-16 增长404、about等页面 -* 2017-1-12 增加代码高亮 -* 2017-3-19 增加打赏公共 -* 2017-9-8 多说评论系统停服,切换到disqus评论(多说上的评论全没了😭) -* 2019-1-25 增加博客底部社交链接 -* 2020-7-8 博客域名从 [http://baixin.io](http://baixin.io) 迁移到 [http://leopardpan.cn](http://leopardpan.cn) (baixin.io没法备案之前的统计也全没了😭). -* 2020-7-13 增加文章自动生成目录功能, 增加livere评论系统 - -## 博客更新手册 - -如果你已经在使我的博客模板了,现在又想继续更新新版的博客,你下载博客后只需要修改两个各方 - -* _config.yml 文件 -* _posts 目录 - -`_config.yml` 文件里面的内容设置成你自己的,也可以用你原来的,最好是更新版的字段做一下对比。 - -`_posts` 目录里面的文章全部删掉,把你自己的文章放进来 - -修改以上两个地方新版的博客便更新完成,快看看效果吧! - - - - - - - - - - - diff --git a/_posts/2020-07-11-blog_info.md b/_posts/2020-07-11-blog_info.md deleted file mode 100644 index b75452398a..0000000000 --- a/_posts/2020-07-11-blog_info.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -layout: post -title: 博客模板功能介绍 -date: 2020-07-11 -tags: jekyll ---- - - -在此之前我有写过一篇[如何使用我博客模板的文章](http://leopardpan.cn/2016/10/jekyll_tutorials1/) 请先看这篇基础集成,这篇文章主要讲的是如何使用和修改我的博客主题,文章内容含以下几点: - -## 博客特性 - -- [x] 适配电脑、手机、平板等各屏幕 -- [x] 响应式设计 -- [x] 个性化头像 -- [x] 每篇文章自动添加打赏功能 -- [x] 支持Disqus、livere评论系统 -- [x] 支持站点总数访问统计,每篇文章访问统计 -- [x] 支持文章自动生成目录 -- [x] 支持标签分类 -- [x] 支持代码高亮 -- [x] 支持文章H1、H2、H3、H4标题样式多样化 -- [x] 支持多种三方社交icon展示,能从博客直接跳转到自己的三方社交主页 -- [x] 支持三方社交分享(facebook、twitter) - - -## 博客主要模块介绍 - -### _config.yml - -`_config.yml` 是博客的配置文件,整个站点的信息都在这修改,想要把我的模板改成你自己的也需要修改`_config.yml` - -**重要字段说明** - -* enableToc: 是否开启文章自动生成目录,设置为false文章不会自动生成目录 -* comment/livere: livere评论系统,支持微信、qq、微博、豆瓣、twitter等登录后可以直接评论 -* comment/disqus: disqus评论系统,支持facebook、twitter等登录后可以直接评论 -* social/weibo、github、zhihu、jianshu等: 个人站底部展示的微博等三方社交按钮,点击后直接跳转到个人微博或其他社交主页 -* baidu/id: 百度统计,用来统计你个人站点的用户访问情况 -* ga/id: google统计,用来统计你个人站点的用户访问情况 - -_config.yml 文件除以上字段还有一些可以自行修改,例如title之类的字段 - -### _posts - -`_posts` 目录是用来存放文章的目录,写新文章,直接放在这个目录即可 - -使用博客模板时,请把博客自带的文章给去掉,如果想使用博客自带的文章请 `注明出处`。 - - -### 自定义页面 - -about.md、support.md 等为自定义页面,如果你想添加自动以页面可以直接复制about.md 文件修改文件名和里面的内容即可。 - -如果需要在导航显示你新增的页面,直接在`_config.yml` 文件的nav字段中添加你新页面配置即可 - -### 修改说明 - -如果要修改博客模板信息建议只修改`_config.yml` 文件内容和 `_posts` 里面的文章信息。因为博客模板一直在更新迭代,改动多了以免你后期更新博客模板的时候不方便。 - -如果你想改动模板的样式又想继续更新迭代博客模板,你可以提交在github上提交`pull request` 或者直接给我发邮件建议改成什么样,如果你的提议确实可以,我会采纳的,并且非常感谢你的建议。 - -博客迭代信息请看[ReleaseNode](http://leopardpan.cn/2020/07/ReleaseNode/) - -遇到解决不了的问题可以找 [技术支持](http://leopardpan.cn/support/) - - - - - diff --git "a/_posts/2022-07-10-D-Tale\345\272\223\344\273\213\347\273\215.md" "b/_posts/2022-07-10-D-Tale\345\272\223\344\273\213\347\273\215.md" new file mode 100644 index 0000000000..5dd83a9af3 --- /dev/null +++ "b/_posts/2022-07-10-D-Tale\345\272\223\344\273\213\347\273\215.md" @@ -0,0 +1,31 @@ +--- +layout: post +title: "当我不会写代码时候,我在干什么(一):D-Tale库介绍" +date: 2022-07-10 +description: "D-Tale生成交互式图形界面,支持在其中定义所需的数据外观,并根据需要对数据进行探索性分析。" +tag: 数据分析 +--- + +## D-Tale是什么? + +Pandas数据框以带有行和列的表格格式表示。它与表格中数据的excel显示非常相似。excel有很多可以操作的界面,比如说数据排序,数据筛选,数据聚合,生成各种图片以及数据透视表等等。Pandas dataframe 和 excel 的显着区别在于,在 Pandas 中,您不能直接对数据集进行修改。您既不能通过任何 GUI 对数据进行排序,也不能通过选择一些参数来创建快速可视化。 + +Pandas可以定制化地进行数据探索,但是要记住一些基本的语句,比如说怎么导入导出到csv,怎么进行索引等等,这不太友好;还有的时候,你希望进行一些漫无目的的探索,但是又懒得去看帮助文档或者感觉得到的信息针对性不强。你可能会希望有个GUI可视化界面,操作就和Excel一样简单,并且可以把操作过程记录下来,获得数据探索、数据清洗和绘图的代码,下次面对另外的数据也可以重复该过程。 + +这就是**D-Tale**!!!D-Tale是一个强大的库,它它与jupyter notebooks和python/ipython终端无缝集成,支持诸如DataFrame,Series,MultiIndex,DateTimeIndex和RangeIndex之类的Pandas对象,可以用很轻量的代码量完成日常所需要的数据分析和可视化工作。 + +## 插个楼:Pandas是啥? + +### Pandas是啥 + +Pandas 是一个开放源码、BSD 许可的库,提供高性能、易于使用的数据结构和数据分析工具。 + +Pandas 名字衍生自术语 "panel data"(面板数据)和 "Python data analysis"(Python 数据分析)。 + +Pandas 一个强大的分析结构化数据的工具集,基础是 Numpy(提供高性能的矩阵运算)。 + +Pandas 可以从各种文件格式比如 CSV、JSON、SQL、Microsoft Excel 导入数据。 + +Pandas 可以对各种数据进行运算操作,比如归并、再成形、选择,还有数据清洗和数据加工特征。 + +Pandas 广泛应用在学术、金融、统计学等各个数据分析领域。 \ No newline at end of file diff --git "a/_posts/2022-07-10-markdown\347\256\200\344\273\213.md" "b/_posts/2022-07-10-markdown\347\256\200\344\273\213.md" new file mode 100644 index 0000000000..ac6b9e4916 --- /dev/null +++ "b/_posts/2022-07-10-markdown\347\256\200\344\273\213.md" @@ -0,0 +1,289 @@ +--- +layout: post +title: "markdown基本功能介绍" +date: 2022-07-10 +description: "markdown基本功能介绍" +tag: markdown +--- + +### 标题表示方法 + +最多可以使用六级标题 + +``` +# 一级标题 +## 二级标题 +### 三级标题 +``` + +### 在 vs code中使用md文件 + +1. 如何在 vs code中使用md文件以及查看: 先点击左上角,选择分栏;然后在右边的分栏中,按下快捷键“control+shift+v”预览md文件 +2. 或者下载"markdown all in one"插件,方便浏览 + + +### 表格 | table + +|列1|列2|列3| +|-|-|-| +|行1|行1|行1| +|行2|行2|行2| +|行3|行3|行3| + +``` +#### 表格 | table + +|列1|列2|列3| +|-|-|-| +|行1|行1|行1| +|行2|行2|行2| +|行3|行3|行3| +``` + +冒号在右边表示右对齐 + +|列1|列2|列3| +|-:|-:|-:| +|行1|行1|行1| +|行2|行2|行2| +|行3|行3|行3| + +``` +冒号在右边表示右对齐 + +|列1|列2|列3| +|-:|-:|-:| +|行1|行1|行1| +|行2|行2|行2| +|行3|行3|行3| +``` + +冒号在左边表示左对齐 + +|列1|列2|列3| +|:-|:-|:-| +|行1|行1|行1| +|行2|行2|行2| +|行3|行3|行3| + +``` +冒号在左边表示左对齐 + +|列1|列2|列3| +|:-|:-|:-| +|行1|行1|行1| +|行2|行2|行2| +|行3|行3|行3| +``` + +两边都有冒号表示中间对齐 + +|列1|列2|列3| +|:-:|:-:|:-:| +|行1|行1|行1| +|行2|行2|行2| +|行3|行3|行3| + +``` +两边都有冒号表示中间对齐 + +|列1|列2|列3| +|:-:|:-:|:-:| +|行1|行1|行1| +|行2|行2|行2| +|行3|行3|行3| +``` + + + +### 列表 + +无序列表 + +* 无序列表 + * 无序列表2 + * 无序列表3 + +``` +无序列表 + +* 无序列表 + * 无序列表2 + * 无序列表3 +``` + +有序列表 + +1. 有序列表 + 1. 有序列表1 +2. 有序列表 + 1. 有序列表2 + 2. 有序列表2 + +``` +有序列表 + +1. 有序列表 + 1. 有序列表1 +2. 有序列表 + 1. 有序列表2 + 2. 有序列表2 +``` + +任务列表: + +我明天要做的事情 +- [ ] 吃饭 +- [x] 睡觉 + +``` +任务列表: + +我明天要做的事情 +- [ ] 吃饭 +- [x] 睡觉 +``` + + +### 注解和脚注 + +注解 +> 这里的内容表示注解 + +``` +注解 +> 这里的内容表示注解 +``` + + +脚注: +这是一个链接到谷歌的[^脚注]。 + +[^脚注]: http://www.google.com + +``` +脚注: +这是一个链接到谷歌的[^脚注]。 + +[^脚注]: http://www.google.com +``` + + + +### 图片和超链接 + +超链接: +1. 内联的方式: 中括号+小括号 + * This is an [example link](http://www.google.com). +2. 引用的方式:这样就可以使用[文本]+[id]的方式进行引用 + * I get 10 times more traffic from [Google][1] than from [Yahoo][2] or [MSN][3]. +3. 还可以链接到标题 + * 请参考[标题1](#一级标题) + +[1]: http://google.com/ "Google" +[2]: http://search.yahoo.com/ "Yahoo Search" +[3]: http://search.msn.com/ "MSN Search" + +``` +超链接: +1. 内联的方式: 中括号+小括号 + * This is an [example link](http://www.google.com). +2. 引用的方式:这样就可以使用[文本]+[id]的方式进行引用 + * I get 10 times more traffic from [Google][1] than from [Yahoo][2] or [MSN][3]. +3. 还可以链接到标题 + * 请参考[标题1](#一级标题) + +[1]: http://google.com/ "Google" +[2]: http://search.yahoo.com/ "Yahoo Search" +[3]: http://search.msn.com/ "MSN Search" +``` + +图片格式:比起超链接多了一个感叹号, +* 小括号内可以使用URL地址或者本地文件的相对路径 +* 可以空一格之后添加""表示图片的属性,鼠标放上来就可以使用了 +* 和超链接的区别在于多了一个感叹号 +![浙大logo](https://upload.wikimedia.org/wikipedia/zh/thumb/1/16/Zhejiang_University_Logo.svg/1200px-Zhejiang_University_Logo.svg.png "浙大logo"){:height="40%" width="40%"} + +``` +图片格式:比起超链接多了一个感叹号, +* 小括号内可以使用URL地址或者本地文件的相对路径 +* 可以空一格之后添加""表示图片的属性,鼠标放上来就可以使用了 +* 和超链接的区别在于多了一个感叹号 +![浙大logo](https://upload.wikimedia.org/wikipedia/zh/thumb/1/16/Zhejiang_University_Logo.svg/1200px-Zhejiang_University_Logo.svg.png "浙大logo"){:height="40%" width="40%"} +``` + +嵌入视频 + +``` + +``` + + + +### 文字格式 + +这里是*斜体* + +``` +这里是*斜体* +``` + +这里是**粗体** +``` +这里是**粗体** +``` + +这里是`变量名`内容 +``` +这里是`变量名`内容 +``` + +下划线: + +要使用HTML中的u标签 + +下划线 + + +``` +下划线: + +要使用HTML中的u标签 + +下划线 +``` + +==文字高亮== + +``` +==文字高亮== +``` + + +代码块 +``` C +int main(){ + return 0; +} + +``` + +删除线: + +~~不太行哦~~ + +``` +删除线: + +~~不太行哦~~ +``` + diff --git "a/_posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275.md" "b/_posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275.md" new file mode 100644 index 0000000000..c70aefec3b --- /dev/null +++ "b/_posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275.md" @@ -0,0 +1,87 @@ +--- +layout: post +title: "海表面高度数据下载" +date: 2022-08-09 +description: "介绍如何从欧洲中心下载海表面高度异常数据" +tag: 物理海洋学 +--- + +### 数据介绍 + +哥白尼海洋服务(Copernicus Marine Service)提供免费开放的海洋数据和服务,助力海洋政策实施,支持蓝色增长和科学创新。 + +其[官网](https://marine.copernicus.eu/)主页如下: + +![CMEMS](/images/posts/2022-08-09-海表面高度数据下载/Copernicus%20Marine%20Service.jpg) + +### 如何选择我们需要的数据 + +首先,我们需要注册一个账号,账号说明使用数据的用途以及我们所属的高校,大概两三天就能审核通过。 + +接着,是检索数据,以我们需要的sea level anomaly (SLA) 数据为例,点击OCEAN PRODUCTS,在General catalogue中输入"SLA"然后点击搜索,以下是呈现结果: + +![搜索SLA返回的结果](/images/posts/2022-08-09-海表面高度数据下载/数据检索.jpg) + +我们发现,第一个数据集(GLOBAL OCEAN GRIDDED L4 SEA SURFACE HEIGHTS AND DERIVED VARIABLES REPROCESSED (1993-ONGOING))可能是我们需要的数据,[这个数据集](https://resources.marine.copernicus.eu/product-detail/SEALEVEL_GLO_PHY_L4_MY_008_047/INFORMATION)涵括了1993年以来高度计卫星栅格海平面异常(海表面高度减去20年的平均值),同时提供了其他的一些变量如ADT(动态地形),尤其是地转流和地转异常(geostrophic currents (absolute and anomalies)),能够节省数据处理的时间,其变量时间分辨率是0.25° × 0.25°分辨率,提供数据下载格式是nc格式。 + +从SLA计算地转流速(UG/VG)是比较基础的内容,有关代码可以看一下这两个博客: + +[matlab版本](https://blog.csdn.net/lisqwakkak/article/details/122332891) + +[python版本](https://blog.csdn.net/weixin_44237337/article/details/120773341) + +![数据页面显示的信息](/images/posts/2022-08-09-海表面高度数据下载/数据介绍.jpg) + +至于ADT和SSH和SLA的关系,可以看一下博客: +[What are the differences between the SSH and SLA?](https://help.marine.copernicus.eu/en/articles/6025269-what-are-the-differences-between-the-ssh-and-sla) + +![Scheme representation](/images/posts/2022-08-09-海表面高度数据下载/概念图.png) + +### 数据在线可视化 + +在初步了解了数据的基本信息,提供的变量类型以及具体属性之后,网站还提供了非常良好的交互式地帮助我们在线可视化变量的工具,点击“view products”即可进入: + +![数据页面显示的信息](/images/posts/2022-08-09-海表面高度数据下载/数据在线查看.jpg) + +在左边的面板,有“add layers",可以选择画图需要的数据,,可以更改colormap的类型,更改显示的日期等等。 + +![东西向地转流速示意图](/images/posts/2022-08-09-海表面高度数据下载/全球东西向流速.jpg) + +### 数据下载 + +同时,我们还发现在左边的面板上有个下载的按钮( + +![下载图](/images/posts/2022-08-09-海表面高度数据下载/下载按钮.jpg) + +下方可以设置选择研究的区域以及选择的时间范围,研究区域的选取可以在地图上选择一个矩形区域或者手动输入东西南北边界来获得: + +![数据范围选择](/images/posts/2022-08-09-海表面高度数据下载/数据范围.jpg) + +如图所示,这个网站提供了直接下载(紫色圆圈)和API下载两种方式(红色圆圈),前者更加简便,后者可以提供较为定制化地下载选择。 + +其中,如果我们想要下载东经100°到东经150°,北纬20°到40°,时间跨度是2022年2月份的海表面东西方向的地转速度,只需要在配置好MOTU服务的基础上,在命令行输入以下指令即可把把数据下载到指定文件夹(OUTPUT_DIRECTORY): + +``` +python -m motuclient --motu https://my.cmems-du.eu/motu-web/Motu --service-id SEALEVEL_GLO_PHY_L4_MY_008_047-TDS --product-id cmems_obs-sl_glo_phy-ssh_my_allsat-l4-duacs-0.25deg_P1D --longitude-min 100 --longitude-max 150 --latitude-min 20 --latitude-max 40 --date-min "2022-02-01 00:00:00" --date-max "2022-02-28 23:59:59" --variable ugos --out-dir --out-name --user --pwd +``` + +![code](/images/posts/2022-08-09-海表面高度数据下载/code.jpg) + +同时,如果我们想要批量下载该研究区域其他时间段或者更大时间尺度范围的海表流场信息,只需要对该段代码进行细微的改动或者增加循环的方式即可! + +#### 可能遇到的问题 + +1. 网络问题和其他 + * On Linux and macOS, please replace double quotes with single quotes. On Windows, use double quotes as shown above. + + * If you use an HTTP proxy, add the following option: + + `--proxy-server :` + + * If your proxy-server requires authentication, add the following options: + + `--proxy-user --proxy-pwd ` + +2. 什么是MOTU Client以及需要的python环境要求? + + [提防python版本问题](https://help.marine.copernicus.eu/en/articles/4796533-what-are-the-motu-client-motuclient-and-python-requirements) diff --git "a/_posts/2022-10-26-\345\211\215\347\253\257\345\205\245\351\227\250.md" "b/_posts/2022-10-26-\345\211\215\347\253\257\345\205\245\351\227\250.md" new file mode 100644 index 0000000000..c09b57934b --- /dev/null +++ "b/_posts/2022-10-26-\345\211\215\347\253\257\345\205\245\351\227\250.md" @@ -0,0 +1,30 @@ +--- +layout: post +title: "前端入门" +date: 2022-10-26 +description: "介绍基本的前端学习语法,比如说HTML,CSS和Javascript学习中的体验和收获" +tag: 前端 + +--- + +### 在VScode中如何配置 + +1. 下载扩展“Live Server”,可以实时刷新和浏览在浏览器中显示的内容 + ![live server](/images/posts/2022-10-26-前端入门/live%20Server.jpg) +2. 记住一些快捷键,比如说使用(!+Enter)可以补全基本的HTML文件信息,比如说(.+ClassName)可以创建一个标签属性为ClassName的块级元素。 + ![!+Enter](/images/posts/2022-10-26-前端入门/开头模板.jpg) + ![.+ClassName](/images/posts/2022-10-26-前端入门/class.jpg) + + +### 简介 + +HTML、CSS和JavaScript的关系如下: + +**HTML是网页的结构,CSS是网页的外观,而JavaScript是页面的行为** + +### HTML基本语法 + + +### CSS基本语法 + +### Javascript \ No newline at end of file diff --git "a/_posts/2022-10-26-\345\246\202\344\275\225\344\275\277\347\224\250GitHub Page\346\220\255\345\273\272\350\207\252\345\267\261\347\232\204\344\270\252\344\272\272\347\275\221\351\241\265.md" "b/_posts/2022-10-26-\345\246\202\344\275\225\344\275\277\347\224\250GitHub Page\346\220\255\345\273\272\350\207\252\345\267\261\347\232\204\344\270\252\344\272\272\347\275\221\351\241\265.md" new file mode 100644 index 0000000000..6ce71017a2 --- /dev/null +++ "b/_posts/2022-10-26-\345\246\202\344\275\225\344\275\277\347\224\250GitHub Page\346\220\255\345\273\272\350\207\252\345\267\261\347\232\204\344\270\252\344\272\272\347\275\221\351\241\265.md" @@ -0,0 +1,271 @@ +--- +layout: post +title: "如何使用GitHub Page搭建自己的个人网页" +date: 2022-10-26 +description: "介绍自己在搭建个人网站时候进行的尝试" +tag: 个人网站 +--- +### 简介 + +一般来说,个人主页是自己的一张名片,有些人用来记录自己的生活,有些人用来分享技术、总结自己踩过的坑,有些人用来展示实验室和研究生招生等等。其实很多类似博客园之类的都可以写文章,没有必要自己弄自己的网站。 + +但是如果想选择自己搭建自己的网站,想选择不一样的样式,那么不妨可以多看看大佬们的主页。 + +对此,我们需要满足两个条件: + +第一,我们可能需要掌握一些前端的编程知识,至少我们需要学会HTML超链接文本的书写,在里面可以展示我们网站想要展示给别人的基本内容;如果需要对网页内容进行美化,则还需要学习CSS(层叠样式表)的基本知识;如果进一步还想和观众进行互动的话,可能还要学习JavaScript的知识,这个其实就跟画PPT一样,我们肯定能学会,但是能不能画好,就是另外一回事了。 + +第二个,就是要从一些运营商购买域名,比如说我同学haris就有自己的域名,还买了上市运营商的股票。但是主要问题是有一定学习门槛,而且我心疼钱呀呜呜呜!!! + +那么,有没有一种方法,能够做到零成本、维护简单、以及不需要额外设计呢?答案是肯定的,GitHub 为我们提供了相应的 Pages 服务。这意味着,只要我们拿到别人已经做好的样式文件(别人写好了css,js等文件,拿来用就行了),再放入自己的 Markdown 文件,就能够建立简易的静态网页,从而实现零成本搭建个人主页的需求。 + +以下是一些我参考过的,对我研究生生涯很有帮助的个人主页: + +1. [炸鸡人博客](https://zhajiman.github.io/) + 1. Python系列 + 2. Cartopy系列 + 3. Matplotlib 系列 + 4. ... +2. [摸鱼的大佬](https://yxy-biubiubiu.github.io/) +3. [Eddy's World](https://iloveocean.top/) + 1. Academic + 2. Life + 1. [关于抑郁症这件事](https://iloveocean.top/index.php/archives/850/) + 2. [关于自律的一个月尝试](https://iloveocean.top/index.php/archives/555/) + 3. [小白入门系列之一:健身增肌训练](https://iloveocean.top/index.php/archives/318/) + 3. Software +4. [抽筋儿的话语权](https://xuchi.name/) +5. [Clarmy吱声](http://clarmy.net/) + +### Jekyll简介 + +我们首先来解决第一个问题,那就是不会一些前端语言,只会最简单的Markdown能不能写出自己的静态网站? + +有滴,套模板就好了。 + +常用的静态网站生成器有:Jekyll,Hugo,Hexo等。Jekyll使用Ruby语言构建,是GitHub Pages默认支持的生成器;Hugo使用Go语言构建,Hexo使用NodeJS技术构建。据说Hugo速度是最快的,Hexo不同版本配置会差不少。 + +为什么最后会选择Jekyll生成器呢? +因为我看上的模板是这样搭建的 + +### 如何使用GitHub Page + +GitHubPages 是 GitHub推出的免费静态网页托管服务,我们可以使用 GitHub 或者 Gitee 托管博客、项目官网等静态网页。它们支持以 Jekyll、Hugo、Hexo 等工具编译而成的静态资源。同时网上也存在着较多的 Jekyll Themes、Hexo Themes 、Hugo Themes 等主题资源。对于轻量的博客需求,我们只需拷贝任意一种自己喜欢的主题,然后上传至自己的仓库,就能够通过 "name.github.io" 或者 "name.gitee.io" 等域名访问自己的博客页面。 + +Gitee据说也可以,但是好像很容易被吞图传 + +当然这个不是必须的,WordPress等其他网站或者自己搞一个自己的域名也是很好的选择。但是,谁让它是免费的呢;而且还有这么多技术博客教你怎么? + +使用github page创建自己的免费个人博客主要包括以下步骤,见[官网步骤](https://pages.github.com/): + +1. 创建一个repository,这个repository一定要命名为username.github.io的格式。 +2. 在本地克隆该repository。 +3. 进入该项目文件夹并进行修改,可以多找找其他人的博客,确定真的喜欢才行。我第一次找了个二次元大佬的博客主页,后来发现不少很适合自己。 +4. 将修改发布到repository:Add, commit, and push your changes。 +5. 刷新一下,应该一两分钟之后就可以在自己的网站看到自己的更改。 +6. 或者可以启动本地服务,现预览一遍可能的效果:jekyll serve然后在Server address: http://127.0.0.1:4000查看效果。 + +### 撰写博文的过程 + +#### 比如说从写一个Markdown简介开始 + +#### Jekyll 环境配置 +安装 jekyll +``` +gem install jekyll +``` +启动本地服务 +``` +jekyll serve +``` +在浏览器中打开 http://127.0.0.1:4000,就可以看到本地的网页了。 + +#### 写完之后你的项目文件夹应该是怎么样的呢? + +Jekyll 的核心其实是一个文本转换引擎。它的概念其实就是: 你用你最喜欢的标记语言来写文章,可以是 Markdown,也可以是 Textile,或者就是简单的 HTML, 然后 Jekyll 就会帮你套入一个或一系列的布局中。在整个过程中你可以设置URL路径, 你的文本在布局中的显示样式等等。这些都可以通过纯文本编辑来实现,最终生成的静态页面就是你的成品了。 + +一个基本的 Jekyll 网站的目录结构一般是像这样的:这是我写完博客之后的文件夹列表(有删减) + +``` +│ .gitignore +│ 404.html +│ about.md +│ archive.html +│ feed.xml +│ Gemfile +│ Gemfile.lock +│ index.html +│ Rakefile +│ README.md +│ support.md +│ tags.html +│ _config.yml +│ +├─.jekyll-cache +│ └─Jekyll +│ └─Cache +│ ├─Jekyll--Cache +│ │ └─b7 +│ │ 9606fb3afea5bd1609ed40b622142f1c98125abcfe89a76a661b0e8e343910 +│ │ +│ └─Jekyll--Converters--Markdown +├─code +│ │ 2022-07-11-D-Tale库介绍.ipynb +│ │ +│ └─.ipynb_checkpoints +│ 2022-07-11-D-Tale库介绍-checkpoint.ipynb +│ +├─css +│ animate.css +│ main.css +│ post.css +│ tomorrow.css +├─images +│ │ avatar.jpg +│ │ background-cover.jpg +│ +├─js +│ highlight.pack.js +│ main.js +│ +├─_includes +│ │ comments.html +│ │ external.html +│ │ footer.html +│ │ head.html +│ │ new-old.html +│ │ pagination.html +│ │ read-more.html +│ │ side-panel.html +│ │ toc.html +│ │ +│ ├─JB +│ │ pages_list +│ │ posts_collate +│ │ setup +│ │ tags_list +│ │ +│ └─styles +│ main.css +│ +├─_layouts +│ default.html +│ page.html +│ post.html +│ +├─_posts +│ 2022-07-10-D-Tale库介绍.md +│ 2022-07-10-markdown简介.md +│ 2022-08-09-海表面高度数据下载.md +│ 2022-10-26-前端入门.md +│ 2022-10-27-如何使用GitHub Page搭建自己的个人网页.md +│ +└─_site + │ 404.html + │ feed.xml + │ index.html + │ Rakefile + │ README.md + │ + ├─2022 + │ ├─07 + │ │ ├─D-Tale库介绍 + │ │ │ index.html + │ │ │ + │ │ └─markdown简介 + │ │ index.html + │ │ + │ └─08 + │ └─海表面高度数据下载 + │ index.html + │ + ├─about + │ index.html + │ + ├─archive + │ index.html + │ + ├─code + │ 2022-07-11-D-Tale库介绍.ipynb + │ + ├─css + │ animate.css + │ main.css + │ post.css + │ tomorrow.css + │ + ├─images + ├─js + │ highlight.pack.js + │ main.js + │ + ├─support + │ index.html + │ + └─tags + index.html + +``` + + +#### 注意事项: +1. 发布时间:有一个凌晨,我文章写好了,也push上去了,也没发现错误,但是左等右等都不见个人主页更新这篇文章。后来才知道,github page应该托管的服务器在美国,用的是美国东部时间可能,也就是说你在今天发布的内容,至少要到中午才能看到。 + +2. 如何支持数学公式:
+ 在_layouts/post.html中,在head标签中加入以下代码: + +``` + + + + + + + +``` + +然后在_config.yml中加入以下代码: + +``` +markdown: kramdown +kramdown: + math_engine: mathjax + syntax_highlighter: rouge + input: GFM + hard_wrap: false + syntax_highlighter_opts: + disable: true +``` + + + + +#### 写完之后如何发布 + +写完所有代码并且在本地查看了效果之后,就可以把本地项目所做的更改push到github上了。在本地项目的根目录下,打开git bash,输入以下命令: + +``` +git add . +git commit -m "提交信息" +git push -u origin master + +``` + + +### 待解决功能 + +1. 评论功能,我之前按照教程去弄,但是好像是个韩国的网站,它给我发了注册信息,但是都被我邮箱垃圾桶照单全收了。两个月之后,我才在垃圾箱里面找到,但是那个时候我已经不知道怎么搞了,真的是“出师未捷身先死。” + +### 参考连接 + +1. [Jekyll搭建个人博客](http://leopardpan.github.io/2016/10/jekyll_tutorials1/) (但是这个大佬好像以及不更新了) +2. [用 Hugo 重新搭建博客](https://zhajiman.github.io/post/rebuild_blog/) +3. \ No newline at end of file diff --git "a/_posts/2022-12-14-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2501_\347\272\277\346\200\247\344\273\243\346\225\260.md" "b/_posts/2022-12-14-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2501_\347\272\277\346\200\247\344\273\243\346\225\260.md" new file mode 100644 index 0000000000..b5707e22fd --- /dev/null +++ "b/_posts/2022-12-14-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2501_\347\272\277\346\200\247\344\273\243\346\225\260.md" @@ -0,0 +1,491 @@ +--- +layout: post +title: "机器学习入门1_线性代数" +date: 2022-12-14 +description: "介绍机器学习入门需要的线性代数知识,如矩阵转置、范数等等" +tag: 机器学习 +--- + +# 00 写在前面 +如何将ipynb文件转为markdown文件? +```shell +jupyter nbconvert --to markdown note.ipynb +``` + +# chapter 01 + + +```python +import numpy as np +# 向量 +v = np.array([1,2,3]) +# 矩阵 +m = np.array([[1,2,3],[4,5,6],[7,8,9]]) +# 张量 +t = np.array([ + [[1,2,3],[4,5,6],[7,8,9]], + [[11,12,13],[14,15,16],[17,18,19]], + [[21,22,23],[24,25,26],[27,28,29]], +]) +print("向量:"+str(v)) +print("矩阵:"+str(m)) +print("张量:"+str(t)) +``` + + 向量:[1 2 3] + 矩阵:[[1 2 3] + [4 5 6] + [7 8 9]] + 张量:[[[ 1 2 3] + [ 4 5 6] + [ 7 8 9]] + + [[11 12 13] + [14 15 16] + [17 18 19]] + + [[21 22 23] + [24 25 26] + [27 28 29]]] + + +## 1.2 矩阵转置 + + + +```python +a = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]]) +a_t = a.transpose() +print(a) +# print one line to separate the two outputs +print('-'*30) +print(a_t) +``` + + [[ 1 2 3] + [ 4 5 6] + [ 7 8 9] + [10 11 12]] + ------------------------------ + [[ 1 4 7 10] + [ 2 5 8 11] + [ 3 6 9 12]] + + +## 1.3 矩阵加法 +有时候允许矩阵和向量相加,得到一个矩阵,本质上是构造了一个将b按行复制的一个新矩阵,这种操作叫做广播(broadcasting)。 + + +## 1.4 矩阵乘法 +矩阵乘法的结果是一个矩阵,其第i行第j列的元素是矩阵A的第i行与矩阵B的第j列的内积。A的形状为m×n,B的形状为n×p,那么A×B的形状为m×p。矩阵乘法的计算可以用下面的公式表示: + $$c[i,j] = a[i,:] * b[:,j]$$ + + +```python +m1 = np.array([[1.0,3.0],[1.0,0.0]]) +m2 = np.array([[1.0,2.0],[3.0,5.0]]) +print(m1) +print(m2) +# 矩阵乘法 +m3 = np.dot(m1,m2) +print(m3) +# 矩阵按元素相乘 +m4 = np.multiply(m1,m2) +# 矩阵按元素相乘 +m5 = m1*m2 +print(m4) +print(m5) +``` + + [[1. 3.] + [1. 0.]] + [[1. 2.] + [3. 5.]] + [[10. 17.] + [ 1. 2.]] + [[1. 6.] + [3. 0.]] + [[1. 6.] + [3. 0.]] + + +## 1.5 单位矩阵 +单位矩阵是一个方阵,对角线上的元素为1,其余元素为0。单位矩阵的作用是不改变矩阵的值,它乘以任何矩阵都等于原矩阵。 + + +```python +np.identity(3) +``` + array([[1., 0., 0.], + [0., 1., 0.], + [0., 0., 1.]]) + + + +## 1.6 矩阵的逆 +矩阵A的逆(inverse)是一个矩阵B,使得A×B=I,其中I是单位矩阵。矩阵A的逆记为$A^{−1}$。如果A是一个方阵,那么A的逆也是一个方阵。如果A的逆存在,那么A的逆也叫做A的伪逆(pseudo-inverse)。 + + +```python +A = [[1,2],[4,5]] +A_inv = np.linalg.inv(A) +print(A_inv) +``` + [[-1.66666667 0.66666667] + [ 1.33333333 -0.33333333]] + + +## 1.7范数 norm +向量$L^P$范数定义为:
+$$||x||_p = (\sum_{i=1}^n |x_i|^p)^{1/p},p\geq 1,p\in\mathbb{R}$$ + +L1范数:向量中各个元素绝对值之和
+$$||x||_1 = \sum_{i=1}^n |x_i|$$ + +L0范数:向量中非零元素的个数
+$$||x||_0 = \sum_{i=1}^n \mathbb{1}(x_i\neq 0)$$ + +L2范数:向量中各个元素平方和的平方根,也叫欧式范数,是向量x到原点的欧几里得距离,有时候也用L2范数的来衡量向量:$x^Tx$.
+$$||x||_2 = \sqrt{\sum_{i=1}^n x_i^2}$$ + +L∞范数:向量中各个元素绝对值的最大值
+$$||x||_\infty = \max_{i=1,\cdots,n}|x_i|$$ + +机器学习中常用的范数有L1范数和L2范数,L1范数用于稀疏性的优化,L2范数用于凸优化。 + +```python +import numpy as np +a = np.array([1.0,3.0,5.0]) +print("向量的2范数:"+str(np.linalg.norm(a,ord=2))) +print("向量的1范数:"+str(np.linalg.norm(a,ord=1))) +print("向量的无穷范数:"+str(np.linalg.norm(a,ord=np.inf))) +# 矩阵才有Frobenius范数 +b = np.array([[1.0,3.0,5.0],[2.0,4.0,6.0]]) +print('向量的Frobenius范数:'+str(np.linalg.norm(b,ord='fro'))) +``` + + 向量的2范数:5.916079783099616 + 向量的1范数:9.0 + 向量的无穷范数:5.0 + 向量的Frobenius范数:9.539392014169456 + + +## 1.8 特征值分解 +如果一个n×n的矩阵A有n组线性无关的单位特征向量{$v^1$, $v^2$, $\cdots$, $v^n$},并且对应的特征值为{$\lambda_1$, $\lambda_2$, $\cdots$, $\lambda_n$},那么矩阵A可以被分解为:
+ $$A = V diag(\lambda) V^{-1}$$ + +不是所有的矩阵都有特征值分解,如果A是一个n×n的矩阵,那么A的特征值分解存在当且仅当A是可逆的且A的行列式不为0。如果A的特征值分解存在,那么A的特征值分解是唯一的。 + + +```python +A = np.array([[1,2,3],[4,5,6],[7,8,9]]) +# 计算特征值 +eig_value,eig_vector = np.linalg.eig(A) +print("特征值:"+str(eig_value)) +print("特征向量:"+str(eig_vector)) +``` + + 特征值:[ 1.61168440e+01 -1.11684397e+00 -1.30367773e-15] + 特征向量:[[-0.23197069 -0.78583024 0.40824829] + [-0.52532209 -0.08675134 -0.81649658] + [-0.8186735 0.61232756 0.40824829]] + + +## 1.9 奇异值分解 +奇异值分解(Singular Value Decomposition,SVD)是一种矩阵分解方法,它将一个矩阵分解为奇异向量和奇异值,它的分解形式如下:
+ $$A = U\Sigma V^T$$ + +若A是m×n的矩阵,那么U是m×m的正交矩阵(其列向量称为左奇异向量),V是n×n的正交矩阵(其列向量称为右奇异向量),$\Sigma$是m×n的对角矩阵(对角线上的元素称为奇异值)。奇异值分解的作用是将一个矩阵分解为三个矩阵的乘积,这三个矩阵分别是正交矩阵和对角矩阵,这样就可以将矩阵的秩降低,从而达到降维的目的。 + +事实上,左奇异向量式$AA^T$的特征向量,右奇异向量是$A^TA$的特征向量,奇异值是$AA^T$特征值的平方根。 + + +```python +A = np.array([[1,2,3],[4,5,6],[7,8,9]]) +U,D,V= np.linalg.svd(A) +print("U:"+str(U)) +print("D:"+str(D)) +print("V:"+str(V)) +``` + + U:[[-0.21483724 0.88723069 0.40824829] + [-0.52058739 0.24964395 -0.81649658] + [-0.82633754 -0.38794278 0.40824829]] + D:[1.68481034e+01 1.06836951e+00 4.41842475e-16] + V:[[-0.47967118 -0.57236779 -0.66506441] + [-0.77669099 -0.07568647 0.62531805] + [-0.40824829 0.81649658 -0.40824829]] + + +## 1.10 PCA分解 +假设我们有m个数据点$x^1,x^2,\cdot,x^m \in \mathbb{R}^n$,每个数据点是一个n维的向量,我们希望将这些数据点投影到一个l维的空间中(降纬之后的损失信息尽可能地少),使得投影后的数据点之间的距离尽可能的大,而投影后的数据点与原始数据点之间的距离尽可能的小。这就是PCA分解。 +PCA分解是线性变化,假设$x^i$分解之后的对应点为$c^i$,那么有:
+ $$f(x) =c $$
+ $$c \approx =g(f(x))$$
+ $$g(c)=Dc,D \in \mathbb{R}^{n\times l}$$
+为了计算方便,我们将这个矩阵的列向量约束为相互正交的;而且,考虑到尺度缩放的问题,我们将这个矩阵的列向量约束为具有单位范数来获得唯⼀解。 +对于给定的x,我们需要找到信息损失最小的$\boldsymbol{c}^{\star}$:
+$$\boldsymbol{c}^{\star}=\arg \min _{\boldsymbol{c}}\|\boldsymbol{x}-g(\boldsymbol{c})\|_2=\arg \min _{\boldsymbol{c}}\|\boldsymbol{x}-g(\boldsymbol{c})\|_2^2$$ + +这里我们用二范数来衡量信息的损失。展开之后我们有:
+$$ +\|\boldsymbol{x}-g(\boldsymbol{c})\|_2^2=(\boldsymbol{x}-g(\boldsymbol{c}))^{\top}(\boldsymbol{x}-g(\boldsymbol{c}))=\boldsymbol{x}^{\top} \boldsymbol{x}-2 \boldsymbol{x}^{\top} g(\boldsymbol{c})+g(\boldsymbol{c})^{\top} g(\boldsymbol{c}) +$$ + +结合 $g(\boldsymbol{c})$ 的表达式, 忽略不依赖 $\boldsymbol{c}$ 的 $\boldsymbol{x}^{\top} \boldsymbol{x}$ 项, 我们有:
+$$ +\begin{aligned} +\boldsymbol{c}^{\star} & =\arg \min _{\boldsymbol{c}}-2 \boldsymbol{x}^{\top} \boldsymbol{D} \boldsymbol{c}+\boldsymbol{c}^{\top} \boldsymbol{D}^{\top} \boldsymbol{D} \boldsymbol{c} \\ +& =\arg \min _{\boldsymbol{c}}-2 \boldsymbol{x}^{\top} \boldsymbol{D} \boldsymbol{c}+\boldsymbol{c}^{\top} \boldsymbol{I}_l \boldsymbol{c} \\ +& =\arg \min _{\boldsymbol{c}}-2 \boldsymbol{x}^{\top} \boldsymbol{D} \boldsymbol{c}+\boldsymbol{c}^{\top} \boldsymbol{c} +\end{aligned} +$$ + + +这里 $\boldsymbol{D}$ 具有单位正交性。 +对 $\boldsymbol{c}$ 求梯度, 并令其为零, 我们有:
+ + +$$ +\begin{aligned} +\nabla_{\boldsymbol{c}}\left(-2 \boldsymbol{x}^{\top} \boldsymbol{D} \boldsymbol{c}+\boldsymbol{c}^{\top} \boldsymbol{c}\right) & =\mathbf{0} \\ +-2 \boldsymbol{D}^{\top} \boldsymbol{x}+2 \boldsymbol{c} & =\mathbf{0} \\ +\boldsymbol{c} & =\boldsymbol{D}^{\top} \boldsymbol{x} +\end{aligned} +$$ + +因此, 我们的编码函数为:
+$$ +f(x)=D^{\top} \boldsymbol{x} +$$ + +此时通过编码解码得到的 $重构\boldsymbol{x}$ 为:
+$$r(x)=g(f(x))=DD^{\top}x$$ + +接下来求解最优的变换 $\boldsymbol{D}$ 。由于我们需要将 $\boldsymbol{D}$ 应用到所有的 $\boldsymbol{x}_i$ 上,所以我们需要最优化: +$$ +\begin{array}{l} +\boldsymbol{D}^{\star}=\arg \min _{\boldsymbol{D}} \sqrt{\sum_{i, j}\left(\boldsymbol{x}_j^{(i)}-r\left(\boldsymbol{x}^{(i)}\right)_j\right)^2} \\ +\text { s.t. } \boldsymbol{D}^{\top} \boldsymbol{D}=\boldsymbol{I}_l +\end{array} +$$ +
+为了方便, 我们考虑 $l=1$ 的情况, 此时问题简化为: +$$ +\begin{array}{l} +\left.\boldsymbol{d}^{\star}=\arg \min _{\boldsymbol{d}} \sum_i\left(\boldsymbol{x}_j^{(i)}-\boldsymbol{d} \boldsymbol{d}^{\top} \boldsymbol{x}^{(i)}\right)\right)^2 \\ +\text { s.t. } \boldsymbol{d}^{\top} \boldsymbol{d}=1 +\end{array} +$$ +
+考虑 $\mathrm{F}$ 范数, 并进一步的推导:
+$$ +\begin{array}{l} +\boldsymbol{d}^{\star}=\arg \max _{\boldsymbol{d}} \operatorname{Tr}\left(\boldsymbol{d}^{\top} \boldsymbol{X}^{\top} \boldsymbol{X} \boldsymbol{d}\right) \\ +\text { s.t. } \boldsymbol{d}^{\top} \boldsymbol{d}=1 +\end{array} +$$ + +优化问题可以用特征值分解来求解。 + +实际计算中,PCA分解的实现是基于SVD分解的,即假设有一个 $m \times n$ 的矩阵 $\boldsymbol{X}$, 数据的均值为零, 即 $\mathbb{E}[\boldsymbol{x}]=0, \boldsymbol{X}$ 对应的无偏样本协方差矩阵: $\operatorname{Var}[\boldsymbol{x}]=\frac{1}{m-1} \boldsymbol{X}^{\top} \boldsymbol{X}^{\mathrm{d}}$ + + +PCA 是通过线性变换找到一个 $\operatorname{Var}[c]$ 是对角矩阵的表示 $\boldsymbol{c}=\boldsymbol{V}^{\top} \boldsymbol{x}$, 矩阵 $\boldsymbol{X}$ 的主成分可以通过奇异值分解 (SVD) 得到, 也就是说主成分是 $\boldsymbol{X}$ 的右奇异向量。假设 $\boldsymbol{V}$ 是 $\boldsymbol{X}=\boldsymbol{U} \boldsymbol{\Sigma} \boldsymbol{V}^{\top}$ 奇异值分解的右奇异向量, 我们得到原来的特征向量方程: +$$ +\boldsymbol{X}^{\top} \boldsymbol{X}=\left(\boldsymbol{U} \boldsymbol{\Sigma} \boldsymbol{V}^{\top}\right)^{\top} \boldsymbol{U} \boldsymbol{\Sigma} \boldsymbol{V}^{\top}=\boldsymbol{V} \boldsymbol{\Sigma}^{\top} \boldsymbol{U}^{\top} \boldsymbol{U} \boldsymbol{\Sigma} \boldsymbol{V}^{\top}=\boldsymbol{V} \boldsymbol{\Sigma}^2 \boldsymbol{V}^{\top} +$$ +因为根据奇异值的定义 $\boldsymbol{U}^{\top} \boldsymbol{U}=\boldsymbol{I}$ 。因此 $\boldsymbol{X}$ 的方差可以表示为: $\operatorname{Var}[\boldsymbol{x}]=\frac{1}{m-1} \boldsymbol{X}^{\top} \boldsymbol{X}=\frac{1}{m-1} \boldsymbol{V} \boldsymbol{\Sigma}^2 \boldsymbol{V}^{\top}$ 。 +所以 $\boldsymbol{c}$ 的协方差满足: $\operatorname{Var}[\boldsymbol{c}]=\frac{1}{m-1} \boldsymbol{C}^{\top} \boldsymbol{C}=\frac{1}{m-1} \boldsymbol{V}^{\top} \boldsymbol{X}^{\top} \boldsymbol{X} \boldsymbol{V}=\frac{1}{m-1} \boldsymbol{V}^{\top} \boldsymbol{V} \boldsymbol{\Sigma}^2 \boldsymbol{V}^{\top} \boldsymbol{V}=\frac{1}{m-1} \boldsymbol{\Sigma}^2$, 因为根据奇异值定义 $\boldsymbol{V}^{\top} \boldsymbol{V}=\boldsymbol{I}$ 。 $\boldsymbol{c}$ 的协方差是对 角的, $c$ 中的元素是彼此无关的。 + + + + + + +```python +# 以 iris 数据为例,展⽰ PCA 的使⽤。 +import pandas as pd +import numpy as np +from sklearn.datasets import load_iris +import matplotlib.pyplot as plt +from sklearn.preprocessing import StandardScaler +%matplotlib inline + + # 载入数据 +iris = load_iris() +df = pd.DataFrame(iris.data, columns=iris.feature_names) +df['label'] = iris.target +df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label'] +# 查看数据 +df.tail() +``` + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sepal lengthsepal widthpetal lengthpetal widthlabel
1456.73.05.22.32
1466.32.55.01.92
1476.53.05.22.02
1486.23.45.42.32
1495.93.05.11.82
+
+ + + + +```python +# 准备数据 +X = df.iloc[:,0:4]# 取前4列为特征 +y =df.iloc[:,4]# 最后一列为标签 +``` + + +```python +class PCA(): + def __init(self): + pass + def fit(self,X,n_components): + n_samples = np.shape(X)[0] + covariance_matrix = 1/(n_samples-1) * (X-X.mean(axis=0)).T.dot(X-X.mean(axis=0)) + # 对协方差矩阵进行特征值分解 + eig_vals, eig_vecs = np.linalg.eig(covariance_matrix) + # 对特征值进行降序排序 + idx = eig_vals.argsort()[::-1] + eig_vals = eig_vals[idx][:n_components] # 取前n个特征值 + # atleast_1d函数将输入转换为至少为1维的数组 + eig_vecs = np.atleast_1d(eig_vecs[:,idx])[:,:n_components] + + # 得到降纬之后的数据 + X_transformed = X.dot(eig_vecs) + return X_transformed + +``` + + +```python +model = PCA() +Y = model.fit(X,2)# 降维到2维 +``` + + +```python +# 降维后的数据,2列,分别是pca1和pca2,columns表示列名 +pcaDf = pd.DataFrame(np.array(Y),columns=['pca1','pca2']) +Df = pd.concat([pcaDf,y],axis=1)# axis =1表示列合并 +ax= plt.figure(figsize = (5,5)) +ax = plt.subplot(1,1,1) +ax.set_xlabel('pca1',fontsize = 15) +ax.set_ylabel('pca2',fontsize = 15) + +targets = [0,1,2] # 3个类别 +colors = ['r','g','b'] # 3种颜色,表示3个类别 +# for target,color in zip(targets,colors): +# ax.scatter(Df[Df['label']==target]['pca1'],Df[Df['label']==target]['pca2'],c=color,s=50) +for target,color in zip(targets,colors): + indicesToKeep = Df['label'] == target + ax.scatter(Df.loc[indicesToKeep,'pca1'],Df.loc[indicesToKeep,'pca2'],c=color,s =50) +ax.legend(targets) +# title +ax.set_title('PCA of IRIS dataset') +ax.grid() +``` + + +![png](/images/posts/2022-12-14-机器学习入门1_线性代数/note_23_0.png) + + + +```python +# 使用sklearn实习PCA +from sklearn.decomposition import PCA +sklearn_pca = PCA(n_components=2) +Y = sklearn_pca.fit_transform(X) +``` + + +```python +PCADf = pd.DataFrame(np.array(Y),columns=['pca1','pca2']) +Df = pd.concat([PCADf,y],axis=1) +ax= plt.figure(figsize = (5,5)) +ax = plt.subplot(1,1,1) +ax.set_xlabel('pca1',fontsize = 15) +ax.set_ylabel('pca2',fontsize = 15) +targets = [0,1,2] # 3个类别 +colors = ['r','g','b'] # 3种颜色,表示3个类别 +for target,color in zip(targets,colors): + indicesToKeep = Df['label'] == target + ax.scatter(Df.loc[indicesToKeep,'pca1'],Df.loc[indicesToKeep,'pca2'],c=color,s =50) +ax.legend(targets) +# title +ax.set_title('PCA of IRIS dataset') +ax.grid() +``` + + +![png](/images/posts/2022-12-14-机器学习入门1_线性代数/note_25_0.png) + + diff --git "a/_posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272.md" "b/_posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272.md" new file mode 100644 index 0000000000..e2c0bcc123 --- /dev/null +++ "b/_posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272.md" @@ -0,0 +1,825 @@ +--- +layout: post +title: "机器学习入门2_概率论" +date: 2022-12-16 +description: "介绍机器学习入门需要的概率论知识和信息论的知识,如高斯分布,entrophy,图模型等等" +tag: 机器学习 +--- + +# 02 概率论与信息论 + +### 2.1.1 概论与随机变量 +频率学派概率(frequency probability):概率和事件发生的频率相关。频率是指事件发生的次数与事件总次数的比值。频率学派概率的缺点是,它只能用于离散事件,而不能用于连续事件。因为连续事件的发生次数是无穷的,而事件总次数是有限的,所以它们的比值是无穷小的,这样就无法用频率来表示概率了。因此,频率学派概率只能用于离散事件,而不能用于连续事件。 +贝叶斯学派概率(bayesian probability):概率和事件发生的先验知识相关。也就是说,概率是对某件事情发生的信心程度的度量。每次事件发生,都会更新先验知识,从而得到后验知识。贝叶斯学派概率的缺点是,它需要先验知识,而先验知识往往是不确定的,所以贝叶斯学派概率也是不确定的。 +随机变量(random variable)变量:一个可能随机取不同值的变量,如,抛硬币的结果,掷骰子的结果,抽取的样本的特征等。随机变量的取值是随机的,但是随机变量的取值是有限的,所以随机变量是离散的。 + +### 2.1.2 随机变量的分布 + +概论质量函数(probability Mass Function),对于离散型变量,我们先定义一个随机变量,然后用概率质量函数来描述这个随机变量的取值分布。概率质量函数是一个函数,它的输入是随机变量的取值,输出是随机变量取这个值的概率。概率质量函数的定义是,对于离散型随机变量X,它的概率质量函数是一个函数P(x),它的定义域是随机变量X的取值集合。 + +如一个离散型x有k个不同的取值,假设x是均匀分布的,那么概率质量函数为:
+$$P(x=x^i)=\frac{1}{k}$$ + +概论密度函数(probability Density Function),对于连续型变量,我们先定义一个随机变量,然后用概率密度函数来描述这个随机变量的取值分布。概率密度函数是一个函数,它的输入是随机变量的取值,输出是随机变量取这个值的概率。概率密度函数的定义是,对于连续型随机变量X,它的概率密度函数是一个函数P(x),它的定义域是随机变量X的取值集合。P(x)是一个非负的函数,它的积分是1,即:
+$$\int_{-\infty}^{\infty}P(x)dx=1$$ +且满足非负性: +$$ \forall x \in \mathbb{X}, P(x)\geq 0$$ + +累计分布函数(cumulative distribution function)表示对小于x的概率进行积分,即:
+$$CDF(x)=\int_{-\infty}^{x}P(x)dx$$ + + + +```python +import numpy as np +import matplotlib.pyplot as plt +from scipy.stats import uniform +%matplotlib inline +``` + + +```python +# generate the sample +fig,ax = plt.subplots(1,1) +# 使用参数loc和scale,可以得到[loc, loc + scale]上的均匀分布,size=1000表示样本数,random_state=123表示随机种子 +r = uniform.rvs(loc=0,scale=1,size=1000,random_state=123) +# density=True表示概率密度,histtype='stepfilled'表示填充,alpha=0.5表示透明度 +ax.hist(r,density=True,histtype='stepfilled',alpha=0.5) +# 均匀分布pdf +x = np.linspace(uniform.ppf(0.01),uniform.ppf(0.99),100) +ax.plot(x,uniform.pdf(x),'r-',lw=5,alpha =0.6,label='uniform pdf') + +``` + + + + + [] + + + + +![png](/images/posts/2022-12-16-机器学习入门2_概率论/output_4_1.png) + + +### 2.1.3 条件概论与条件独立 + +边缘概论(Marginal Probability)如果我们知道了一组变量的联合概论分布,想要了解其中一个子集的概率分布。这种定义在子集上的概率分布成为边缘概论分布:
+$$ \forall x \in \mathbb{X},P(\mathrm{x}=x) = \sum_y P(\mathrm{x}=x,\mathrm{y}=y)$$ + +条件概论(conditional probability)在很多情况下,我们不仅关心一个事件的概率,还关心在另一个事件发生的条件下,这个事件的概率。这种在另一个事件发生的条件下,事件的概率称为条件概论。条件概论的定义是:
+$$P(\mathrm{x}=x|\mathrm{y}=y)=\frac{P(\mathrm{x}=x,\mathrm{y}=y)}{P(\mathrm{y}=y)}$$ +这个公式表示在y发生的条件下,x发生的概率。 + +条件概论的链式法则(chain rule of conditional probability):任何多维随机变量的联合概论分布,都可以分解成只有只有一个变量的条件概论相乘的形式
+$$P(\mathrm{x}=x,\mathrm{y}=y,\mathrm{z}=z)=P(\mathrm{x}=x|\mathrm{y}=y,\mathrm{z}=z)P(\mathrm{y}=y,\mathrm{z}=z)=P(\mathrm{x}=x|\mathrm{y}=y,\mathrm{z}=z)P(\mathrm{y}=y|\mathrm{z}=z)P(\mathrm{z}=z)$$ + +$$P(x_1,x_2,\cdots,x_n)=P(x_1|x_2,\cdots,x_n)P(x_2|x_3,\cdots,x_n)\cdots P(x_{n-1}|x_n)P(x_n)$$ + +独立性(independence)如果两个随机变量X和Y满足:
+$$P(\mathrm{x}=x,\mathrm{y}=y)=P(\mathrm{x}=x)P(\mathrm{y}=y)$$ +也即是联合概论分布可以表示成只有一个变量的条件概论相乘的形式。 + +条件独立性(conditional independence)如果关于x和y的条件概率分布对于z的每一个值都满足独立性,那么我们就说x和y在z的条件下是条件独立的。
+$$P(\mathrm{x}=x,\mathrm{y}=y|\mathrm{z}=z)=P(\mathrm{x}=x|\mathrm{z}=z)P(\mathrm{y}=y|\mathrm{z}=z)$$ + +### 2.1.4 随机变量的度量 +期望(expectation)是随机变量的平均值,期望的定义是:
+$$E(X)=\sum_x xP(X=x)$$ +对于连续型随机变量可以通过积分的形式表示:
+$$E(X)=\int_{-\infty}^{\infty}xP(X=x)dx$$ + +另外,期望是线性的,即:
+$$E(aX+bY)=aE(X)+bE(Y)$$ + +方差(variance)是随机变量的离散程度,方差的定义是:
+$$Var(X)=E[(X-E(X))^2]$$ + +标准差(standard deviation)是方差的平方根,标准差的定义是:
+$$\sigma(X)=\sqrt{Var(X)}$$ + +协方差(covariance)是两个随机变量的线性相关程度,协方差的定义是:
+$$Cov(X,Y)=E[(X-E(X))(Y-E(Y))]$$ + +**注意**协方差的符号表示两个随机变量的线性相关程度,正数表示正相关,负数表示负相关,0表示不相关。
+**注意**独立与零协方差更强,因为独立还排除了非线性的相关。 + + +```python +x= np.array([1,2,3,4,5,6,7,8,9,10]) +y = np.array([10,9,8,7,6,5,4,3,2,1]) +Mean = np.mean(x) +Var = np.var(x) +Var_unbiased = np.var(x,ddof=1)# ddof=1表示无偏估计 +Std = np.std(x) +Cov = np.cov(x,y) +Mean,Var,Var_unbiased,Std,Cov +``` + + + + + (5.5, + 8.25, + 9.166666666666666, + 2.8722813232690143, + array([[ 9.16666667, -9.16666667], + [-9.16666667, 9.16666667]])) + + + +### 2.1.5 常用概率分布 +伯努利分布(Bernoulli distribution)是一个二项分布的特例,它只有两个可能的取值,0和1,它的概率质量函数是(表示一次实验成功的概率):
+$$P(X=1)=p$$, +$$P(X=0)=1-p$$, +$$P(X=x)=p^x(1-p)^{1-x}$$ + + +```python +def plot_distribution(X,axes=None): + '''给定随机变量X,绘制其PDF,PMF,CDF''' + if axes is None: + fig,axes = plt.subplots(1,3,figsize=(12,4)) + x_min,x_max = X.interval(0.99)# 99%的概率密度在区间内 + x = np.linspace(x_min,x_max,1000) + # 连续型变量,画PDF;离散型变量,画PMF + if hasattr(X.dist,'pdf'): + # axes[0]表示第一行,第一列 + axes[0].plot(x,X.pdf(x),label='PDF') + axes[0].fill_between(x,X.pdf(x),alpha=0.5) # 填充,透明度0.5 + else: + x_int = np.unique(x.astype(int))# 取整,unique去重 + axes[0].bar(x_int,X.pmf(x_int),label='PMF') + # CDF + axes[1].plot(x,X.cdf(x),label='CDF') + for ax in axes: + ax.legend() + return axes +``` + + +```python +from scipy.stats import bernoulli +fig,axes = plt.subplots(1,2,figsize=(12,4)) +r = bernoulli(p=0.6) # 生成伯努利分布 +plot_distribution(r,axes) +``` + + + + + array([, + ], + dtype=object) + + + + +![png](/images/posts/2022-12-16-机器学习入门2_概率论/output_10_1.png) + + + +```python +# 生成实验成功的次数 +p = 0.6 +def trials(n_samples): + # 二项分布,n_samples表示实验次数,p表示成功的概率,samples表示成功的次数 + samples = np.random.binomial(n_samples,p) + proba_zero = (n_samples-samples)/n_samples# 0次成功的概率 + proba_one = samples/n_samples# 1次成功的概率 + return [proba_zero,proba_one] +fig,axes = plt.subplots(1,2,figsize=(12,4)) +# 一次实验,伯努利分布 +n_samples = 1 +axes[0].bar([0,1],trials(n_samples),label ='Bernoulli') +axes[0].set_title('n_samples = {}'.format(n_samples)) +# n次实验,二项分布 +n_samples = 1000 +axes[1].bar([0,1],trials(n_samples),label ='Binomial') +axes[1].set_title('n_samples = {}'.format(n_samples)) +for ax in axes: + ax.legend() +``` + + +![png](/images/posts/2022-12-16-机器学习入门2_概率论/output_11_0.png) + + +范畴分布(Categorical distribution,分类分布)是一个多项分布的特例,它的概率质量函数是(表示第k类的概率):
+$$P(X=k)=p_k$$ + +例如每次试验的结果就可以表示成一个k维的向量,每个维度表示一种结果,只有此次试验的结果对应的维度为1,其余维度为0,例如:
+$$P(X=(1,0,0,0))=p_1$$
+$$P(X=(0,1,0,0))=p_2$$
+$$P(X=(0,0,1,0))=p_3$$
+$$P(X=(0,0,0,1))=p_4$$ + + + +```python +def k_possibilities(k): + '''生成k个可能的结果''' + res = np.random.rand(k)# random.rand(k)生成k个[0,1)之间的随机数 + _sum = sum(res) # 求和 + for i,x in enumerate(res): + res[i] = x/_sum # 归一化,enumerate返回索引和值,i表示索引,x表示值 + return res +fig,axes = plt.subplots(1,2,figsize=(12,4)) +# 一次实验,范畴分布 +n_samples = 1 +k=10 +samples = np.random.multinomial(n_samples,k_possibilities(k)) +axes[0].bar(range(len(samples)),samples/n_samples,label ='Multinomial') + +# n次实验,多项分布 +n_samples = 1000 +samples = np.random.multinomial(n_samples,k_possibilities(k)) +axes[1].bar(range(len(samples)),samples/n_samples,label ='Multinomial') +for ax in axes: + ax.legend() +``` + + +![png](/images/posts/2022-12-16-机器学习入门2_概率论/output_13_0.png) + + +高斯分布(Gaussian distribution,正态分布)是一个连续型概率分布,若随机变量$X\sim \mathcal{N}(\mu,\sigma^2)$,它的概率密度函数是:
+$$P(X=x)=\frac{1}{\sqrt{2\pi\sigma^2}}e^{-\frac{(x-\mu)^2}{2\sigma^2}}$$
+有时也会用$\beta = \frac{1}{\sigma^2}$来表示分布的精度(精度越大,分布越狭窄,precision):
+$$P(X=x)=\frac{1}{\sqrt{2\pi\beta^{-1}}}e^{-\frac{(x-\mu)^2}{2\beta}}$$ + +中心极限定理(central limit theorem)是一个重要的定理,它表示当样本量足够大时,样本均值的分布会趋近于正态分布,如噪声的分布就是一个典型的例子,噪声的分布是均匀分布,但是当噪声的数量足够多时,噪声的均值的分布就会趋近于正态分布。 + +如果我们对随机变量进行标准化,即将随机变量减去均值并除以标准差,那么标准化后的随机变量的均值为0,标准差为1,这样的随机变量称为标准正态分布(standard normal distribution),
+$$Z=\frac{X-\mu}{\sigma}$$ + + +```python +from scipy.stats import norm +fig,axes = plt.subplots(1,2,figsize= (12,4)) +mu,sigma = 0,1# 均值,标准差 +r = norm(loc=mu,scale=sigma)# 生成正态分布 +plot_distribution(r,axes) #绘制r的PDF和CDF +``` + + + + + array([, + ], + dtype=object) + + + + +![png](/images/posts/2022-12-16-机器学习入门2_概率论/output_15_1.png) + + +多元高斯分布(multivariate Gaussian distribution)是一个多维的高斯分布,它的概率密度函数是:
+$$P(X=x;\sigma,\Sigma)=\frac{1}{(2\pi)^{n/2}|\Sigma|^{1/2}}e^{-\frac{1}{2}(x-\mu)^T\Sigma^{-1}(x-\mu)}$$ +
+具体推导过程如下:
+先假设 $\mathrm{n}$ 个变量 $x=\left[x_1, x_2, \cdots, x_n\right]^{\mathrm{T}}$ 互不相关,且服从正态分布 (维度不相关多元正态分布),各个维度的均值 $E(x)=\left[\mu_1, \mu_2, \cdots, \mu_n\right]^{\mathrm{T}}$ ,方差 $\sigma(x)=\left[\sigma_1, \sigma_2, \cdots, \sigma_n\right]^{\mathrm{T}}$ +
+根据联合概率密度公式: +$$ +\begin{aligned} +& f(x)=p\left(x_1, x_2 \ldots x_n\right)=p\left(x_1\right) p\left(x_2\right) \ldots p\left(x_n\right)=\frac{1}{(\sqrt{2 \pi})^n \sigma_1 \sigma_2 \cdots \sigma_n} e^{-\frac{\left(x_1-\mu_1\right)^2}{2 \sigma_1^2}-\frac{\left(x_2-\mu_2\right)^2}{2 \sigma_2^2} \cdots-\frac{\left(x_n-\mu_n\right)^2}{2 \sigma_n}} \\ +& \text { 令 } z^2=\frac{\left(x_1-\mu_1\right)^2}{\sigma_1^2}+\frac{\left(x_2-\mu_2\right)^2}{\sigma_2^2} \cdots+\frac{\left(x_n-\mu_n\right)^2}{\sigma_n^2}, \sigma_z=\sigma_1 \sigma_2 \cdots \sigma_n +\end{aligned} +$$ +这样多元正态分布又可以写成一元那种漂亮的形式了(注意一元与多元的差别):
+$$ +f(z)=\frac{1}{(\sqrt{2 \pi})^n \sigma_z} e^{-\frac{z^2}{2}} +$$ +
+因为多元正态分布有着很强的几何思想,单纯从代数的角度看待z很难看出 z的概率分布规律,这里需要转换成矩阵形式:
+$$ +z^2=z^{\mathrm{T}} z=\left[x_1-\mu_1, x_2-\mu_2, \cdots, x_n-\mu_n\right]\left[\begin{array}{cccc} +\frac{1}{\sigma_1^2} & 0 & \cdots & 0 \\ +0 & \frac{1}{\sigma_2^2} & \cdots & 0 \\ +\vdots & \cdots & \cdots & \vdots \\ +0 & 0 & \cdots & \frac{1}{\sigma_n^2} +\end{array}\right]\left[x_1-\mu_1, x_2-\mu_2, \cdots, x_n-\mu_n\right]^{\mathrm{T}} +$$ +
+等式比较长,让我们要做一下变量替换:
+$$ +x-\mu_x=\left[x_1-\mu_1, x_2-\mu_2, \cdots, x_n-\mu_n\right]^{\mathrm{T}} +$$ +
+定义一个符号 +$$ +\sum=\left[\begin{array}{cccc} +\sigma_1^2 & 0 & \cdots & 0 \\ +0 & \sigma_2^2 & \cdots & 0 \\ +\vdots & \cdots & \cdots & \vdots \\ +0 & 0 & \cdots & \sigma_n^2 +\end{array}\right] +$$ +
+$\sum$ 代表变量 $\mathrm{X}$ 的协方差矩阵, i行j列的元素值表示 $x_i$ 与 $x_j$ 的协方差 +因为现在变量之间是相互独立的,所以只有对角线上 $(i=j)$ 存在元素,其他地方都等于 0 ,且 $x_i$ 与它本身的协方差就等于方差。 +
+
+$\sum$ 是一个对角阵,根据对角矩阵的性质,它的逆矩阵:
+$$ +\left(\sum\right)^{-1}=\left[\begin{array}{cccc} +\frac{1}{\sigma_1^2} & 0 & \cdots & 0 \\ +0 & \frac{1}{\sigma_2^2} & \cdots & 0 \\ +\vdots & \cdots & \cdots & \vdots \\ +0 & 0 & \cdots & \frac{1}{\sigma_n^2} +\end{array}\right]. +$$ +
+对角矩阵的行列式 $=$ 对角元素的乘积 +$$ +\sigma_z=\left|\sum\right|^{\frac{1}{2}}=\sigma_1 \sigma_2 \ldots \sigma_n +$$ +
+替换变量之后,等式可以简化为: +$$ +z^{\mathrm{T}} z=\left(x-\mu_x\right)^{\mathrm{T}} \sum^{-1}\left(x-\mu_x\right) +$$ +
+代入以z为自变量的标准高斯分布函数中: +$$ +f(z)=\frac{1}{(\sqrt{2 \pi})^n \sigma_z} e^{-\frac{z^2}{2}}=\frac{1}{(\sqrt{2 \pi})^n \sum^{\frac{1}{2}}} e^{-\frac{\left(x-\mu_x\right)^T(\Gamma)^{-1}\left(x-\mu_x\right)}{2}} +$$ +
+注意前面的系数变化: 从非标准正态分布 $->$ 标准正态分布需要将概率密度函数的高度压缩 $\left|\sum\right|^{\frac{1}{2}}$ 倍,从一维 $\rightarrow$ n维的过 程中,每增加一维,高度将压缩 $\sqrt{2 \pi}$ 倍 + +reference: +https://www.cnblogs.com/bingjianing/p/9117330.html + + +```python +from scipy.stats import multivariate_normal +x,y = np.mgrid[-1:1:.01,-1:1:.01]# 生成网格点,步长为0.01 +pos = np.dstack((x,y))# 生成网格点坐标,shape=(200,200,2) +sigma = [[2,0.3],[0.3,0.5]] # 协方差矩阵 +mu = [0.5,0.2] +X = multivariate_normal(mu,sigma)# 生成多元正态分布 +fig,axes = plt.subplots(1,1,figsize=(12,4)) +axes.contourf(x,y,X.pdf(pos))# 绘制等高线,pos表示在网格点上的概率密度 +``` + + + + + + + + + +![png](/images/posts/2022-12-16-机器学习入门2_概率论/output_17_1.png) + + +指数分布(Exponential distribution)是描述泊松过程中的事件之间的时间的概率分布,即事件以恒定平均速率连续且独立地发生的过程。 这是伽马分布的一个特殊情况。 它是几何分布的连续模拟,它具有无记忆(Memoryless Property)的关键性质,也即是$P(T>t+s|T>t)=P(T>s)$。 +
+除了用于分析泊松过程外,还可以在其他各种环境中找到,其概率密度函数为: +
+$$ +p(x)=\left\{\begin{array}{c} +\frac{1}{\theta} e^{-\frac{x}{\theta}}, x>0 \\ +0, x \leq 0 +\end{array} \quad(\theta>0)\right. +$$ +
+则称随机变量$X$ 服从参数为 $\theta$ 的指数分布, 记为 $X \sim \operatorname{EXP}(\theta)$. +
+其中 $\lambda = \frac{1}{\theta}$ 称为率参数(rate parameter)。 +
+即每单位时间内发生某事件的次数(比方说:如果你平均每个小时接到2次电话,那么你预期等待每一次电话的时间是半个小时)。指数分布的期望和方差分别为 $\theta$ 和 $\theta^2$. + +指数分布的累计分布函数为:
+$$ P(X \leq x) = 1 - e^{-\frac{x}{\theta}} $$ + + + +```python +from scipy.stats import expon +fig,axes = plt.subplots(1,2,figsize=(12,4)) +r = expon(scale=2)# 生成指数分布,scale=2表示λ=1/2 +plot_distribution(r,axes) + +``` + + + + + array([, + ], + dtype=object) + + + + +![png](/images/posts/2022-12-16-机器学习入门2_概率论/output_19_1.png) + + +拉普拉斯分布(Laplace distribution)是一种连续概率分布,它是双指数分布的特殊情况。它是一种对称分布,其概率密度函数为:
+$$ p(x) = \frac{1}{2 \lambda} e^{ - \frac{|x - \mu|}{\lambda}} $$ + +其中 $\mu$ 称为分布的位置参数(location parameter),$\lambda$ 称为分布的尺度参数(scale parameter)。拉普拉斯分布的期望和方差分别为 $\mu$ 和 $2 \lambda^2$. + + +```python +from scipy.stats import laplace +fig,axes = plt.subplots(1,2,figsize=(12,4)) +mu,gamma = 0,1 # 均值,标准差 +r = laplace(loc=0,scale=1)# 生成拉普拉斯分布 +plot_distribution(r,axes) +``` + + + + + array([, + ], + dtype=object) + + + + +![png](/images/posts/2022-12-16-机器学习入门2_概率论/output_21_1.png) + + +Dirac 分布:Dirac delta function,也称为 Dirac 分布。 + +Dirichlet分布 +基础知识:conjugate priors共轭先验 +共轭先验是指这样一种概率密度:它使得后验概率的密度函数与先验概率的密度函数具有相同的函数形式。它极大地简化了贝叶斯分析。 +如何解释这句话。由于$P(u|D) = p(D|u)p(u)/p(D)$,其中D是给定的一个样本集合,因此对其来说p(D)是一个确定的值,可以理解为一个常数。P(u|D)是后验概率----即观察到一系列样本数据后模型参数服从的概率,p(D|u)是似然概率----在给定的模型参数u下样本数据服从这一概率模型的相似程度,p(u)是u的先验概率----在我们一无所知的情况下u的概率分布。P(u|D)的函数形式完全由p(D|u)和p(u)的乘积决定。如果p(u)的取值使p(u|D)和p(D|u)相同的表达形式(关于u的表达形式),就称p(u)为共轭先验。 + +reference:
+[机器学习的数学基础(1)--Dirichlet分布](https://blog.csdn.net/jwh_bupt/article/details/8841644) + + +Beta分布式二项分布的共轭先验分布. + +Dirichlet分布是一个多维的概率分布,它是一组(多元)独立的 $\operatorname{Beta}$ 分布的共轭先验分布。在贝叶斯推断(Bayesian inference)中,狄利克雷分布作为多项分布的共轭先验得到应用,在机器学习(machine learning)中被用于构建狄利克雷混合模型(Dirichlet mixture model)是一类在实数域以正单纯形(standard simplex)为 支撑集(support)的高维连续概率分布,是Beta分布在高维情形的推广。 + +reference:
+[理解Gamma分布、Beta分布与Dirichlet分布 +](https://zhuanlan.zhihu.com/p/37976562) +
+[最通俗易懂的白话狄利克雷过程(Dirichlet Process)](https://zhuanlan.zhihu.com/p/76991275) + + + +### 2.1.6 常见函数的有用性质 + +logisitc sigmoid函数通常用来产生伯努利分布的概率,因为它的范围是 (0, 1),因此它的导数是伯努利分布的概率密度函数。sigmoid 函数在变量取绝对值⾮常⼤的正值或负值时会出现饱和 (Saturate)现象,意味着函数会变得很平,并且对输⼊的微⼩改变会变得不敏感。
+$$ sigmoid(x) = \frac{1}{1+e^{-x}} $$ + +softplus函数
+$$ softplus(x) = \log(1+e^x) $$ + +因为它的范围是 (0, ∞),softplus函数可以用来产生正太分布的$\beta$和$\sigma$参数,因此它的导数是正太分布的概率密度函数。 +softplus函数名称来源于它是一个平滑的ReLU函数,ReLU函数是一个非线性函数,它的定义是 +$$ ReLU(x) = max(0, x) $$ + + + +```python +x = np.linspace(-10,10,1000) +sigmoid = 1/(1+np.exp(-x)) +softplus = np.log(1+np.exp(x)) +fig,axes = plt.subplots(1,2,figsize=(12,4)) +axes[0].plot(x,sigmoid) +axes[0].set_title('Sigmoid') +axes[1].plot(x,softplus) +axes[1].set_title('Softplus') + +``` + + + + + Text(0.5, 1.0, 'Softplus') + + + + +![png](/images/posts/2022-12-16-机器学习入门2_概率论/output_26_1.png) + + +## 2.2 信息论 +信息论背后的思想:一件不太可能的事件比一件比较可能的事件更有信息量。(信息量的度量就等于不确定性的多少。)
+信息 (Information) 需要满⾜的三个条件: +* ⽐较可能发⽣的事件的信息量要少 +* ⽐较不可能发⽣的事件的信息量要⼤ +* 独⽴发⽣的事件之间的信息量应该是可以叠加的。例如,投掷的硬币两次正⾯朝上传递的信息量,应该是投掷⼀次硬币正⾯朝上的信息量的两倍 + +自信息 (Self-information) 是信息论中的基本概念,它是指单个事件发⽣的信息量。自信息的定义是
+$$ I(x) = -\log P(x) $$ + +香农熵 (Shannon Entropy)是对于整个概论分布P,随机变量的不确定性的衡量:
+$$ H(P) = -( p_1 * \log p_1 + p_2 * \log p_2 + \cdots + p_n * \log p_n) =-\sum_{x}P(x)\log P(x) $$ + +联合熵 (Joint Entropy) 是对于两个随机变量X和Y,它们的联合分布P(X, Y),随机变量的不确定性的衡量:
+$$ H(X, Y) = -\sum_{x,y}P(x, y)\log P(x, y) $$ + +条件熵 (Conditional Entropy) 是对于两个随机变量X和Y,它们的联合分布P(X, Y),随机变量的不确定性的衡量:
+$$ H(Y|X) = -\sum_{x,y}P(x, y)\log P(y|x) $$ + +互信息 (Mutual Information) 表示两个信息相交的部分:
+$$ I(X;Y) = H(X)+H(Y) - H(X,Y) $$ + +信息变差(Variation of Information) 表示两个事件的信息不想交的部分:
+$$ VI(X;Y) = H(X)+H(Y) - 2I(X;Y) $$ + + +```python +p = np.linspace(1e-6,1-1e-6,1000) # 生成概率 +entropy = -p*np.log(p)-(1-p)*np.log(1-p) # 香农熵 (Shannon Entropy) +plt.figure(figsize=(4,4)) +plt.plot(p,entropy) +plt.title('Shannon Entropy') + +``` + + + + + Text(0.5, 1.0, 'Shannon Entropy') + + + + +![png](/images/posts/2022-12-16-机器学习入门2_概率论/output_28_1.png) + + +信息增益(Kullback–Leibler divergence)又称information divergence,information gain,relative entropy 或者KLIC。 +在概率论和信息论中,信息增益是非对称的,用以度量两种概率分布P和Q的差异。
+$$ D_{KL}(P||Q) = \sum_{x}P(x)\log \frac{P(x)}{Q(x)} $$ + +交叉熵 (Cross Entropy) 假设P是真实的分布,Q是预测的分布,那么交叉熵就是真实分布和预测分布之间的距离:
+$$ H(P, Q) = H(P) + D_{KL}(P||Q) = -\sum_{x}P(x)\log Q(x) $$ + + +```python +# D_{KL}(P||Q) 与D_{KL}(Q||P)比较 +x = np.linspace(1,8,500) +y1 = norm.pdf(x,loc =3,scale = 0.5) # loc=3,scale=0.5表示均值为3,标准差为0.5 +y2 = norm.pdf(x,loc =6,scale = 0.5) +p = y1+y2 #构造p(x) +KL_pq ,KL_qp = [],[] +q_list = [] +for mu in np.linspace(0,10,50): + for sigma in np.linspace(0.1,1,50): # 生成50*50个q(x),寻找最优q(x) + q = norm.pdf(x,loc =mu,scale = sigma) # 构造q(x) + q_list.append(q) + KL_pq.append(np.sum(p*np.log(p/q))) # D_{KL}(P||Q) + KL_qp.append(np.sum(q*np.log(q/p))) # D_{KL}(Q||P) +# min +min_KL_pq = np.argmin(KL_pq) # 最小值的索引 +min_KL_qp = np.argmin(KL_qp) + +fig,axes = plt.subplots(1,2,figsize=(12,4)) +axes[0].set_ylim(0,0.8) +axes[0].plot(x,p/2,'b',label='p(x)') +axes[0].plot(x,q_list[min_KL_pq],'r',label='$q^*(x)$') +axes[0].set_title('min D_{KL}(P||Q) ') +axes[0].set_xlabel('x') +axes[0].set_ylabel('p(x)') + +axes[1].set_ylim(0,0.8) +axes[1].plot(x,p/2,'b',label='p(x)') +axes[1].plot(x,q_list[min_KL_qp],'r',label='$q^*(x)$') +axes[1].set_title('min D_{KL}(Q||P)') +axes[1].set_xlabel('x') +axes[1].set_ylabel('p(x)') +``` + + :12: RuntimeWarning: divide by zero encountered in true_divide + KL_pq.append(np.sum(p*np.log(p/q))) # D_{KL}(P||Q) + :12: RuntimeWarning: overflow encountered in true_divide + KL_pq.append(np.sum(p*np.log(p/q))) # D_{KL}(P||Q) + :13: RuntimeWarning: divide by zero encountered in log + KL_qp.append(np.sum(q*np.log(q/p))) # D_{KL}(Q||P) + :13: RuntimeWarning: invalid value encountered in multiply + KL_qp.append(np.sum(q*np.log(q/p))) # D_{KL}(Q||P) + + + + + + Text(0, 0.5, 'p(x)') + + + + +![png](/images/posts/2022-12-16-机器学习入门2_概率论/output_30_2.png) + + +信息增益 (Information Gain) 是指在知道特征X的条件下,特征Y的不确定性减少的程度:
+$$ IG(X;Y) = H(Y) - H(Y|X) $$ + + +```python +import numpy as np +import pandas as pd +from math import log + +def create_data(): + datasets = [['青年', '否', '否', '一般', '否'], + ['青年', '否', '否', '好', '否'], + ['青年', '是', '否', '好', '是'], + ['青年', '是', '是', '一般', '是'], + ['青年', '否', '否', '一般', '否'], + ['中年', '否', '否', '一般', '否'], + ['中年', '否', '否', '好', '否'], + ['中年', '是', '是', '好', '是'], + ['中年', '否', '是', '非常好', '是'], + ['中年', '否', '是', '非常好', '是'], + ['老年', '否', '是', '非常好', '是'], + ['老年', '否', '是', '好', '是'], + ['老年', '是', '否', '好', '是'], + ['老年', '是', '否', '非常好', '是'], + ['老年', '否', '否', '一般', '否'], + ] + labels = [u'年龄', u'有工作', u'有自己的房子', u'信贷情况', u'类别'] + # 返回数据集和每个维度的名称 + return datasets, labels + + +# 熵 +def calc_ent(datasets): + data_length = len(datasets) + label_count = {} + for i in range(data_length): + label = datasets[i][-1] + if label not in label_count: + label_count[label] = 0 + label_count[label] += 1 + ent = -sum([(p / data_length) * log(p / data_length, 2) for p in label_count.values()]) + return ent + + +# 经验条件熵 +def cond_ent(datasets, axis=0): + data_length = len(datasets) + feature_sets = {} + for i in range(data_length): + feature = datasets[i][axis] + if feature not in feature_sets: + feature_sets[feature] = [] + feature_sets[feature].append(datasets[i]) + cond_ent = sum([(len(p) / data_length) * calc_ent(p) for p in feature_sets.values()]) + return cond_ent + + +# 信息增益 +def info_gain(ent, cond_ent): + return ent - cond_ent + + +def info_gain_train(datasets): + count = len(datasets[0]) - 1 + ent = calc_ent(datasets) + best_feature = [] + for c in range(count): + c_info_gain = info_gain(ent, cond_ent(datasets, axis=c)) + best_feature.append((c, c_info_gain)) + print('特征({}) - info_gain - {:.3f}'.format(labels[c], c_info_gain)) + # 比较大小 + best_ = max(best_feature, key=lambda x: x[-1]) + return '特征({})的信息增益最大,选择为根节点特征'.format(labels[best_[0]]) + +datasets, labels = create_data() +train_data = pd.DataFrame(datasets, columns=labels) +print(train_data) +print('特征信息增益为:', info_gain_train(np.array(datasets))) +``` + + 年龄 有工作 有自己的房子 信贷情况 类别 + 0 青年 否 否 一般 否 + 1 青年 否 否 好 否 + 2 青年 是 否 好 是 + 3 青年 是 是 一般 是 + 4 青年 否 否 一般 否 + 5 中年 否 否 一般 否 + 6 中年 否 否 好 否 + 7 中年 是 是 好 是 + 8 中年 否 是 非常好 是 + 9 中年 否 是 非常好 是 + 10 老年 否 是 非常好 是 + 11 老年 否 是 好 是 + 12 老年 是 否 好 是 + 13 老年 是 否 非常好 是 + 14 老年 否 否 一般 否 + 特征(年龄) - info_gain - 0.083 + 特征(有工作) - info_gain - 0.324 + 特征(有自己的房子) - info_gain - 0.420 + 特征(信贷情况) - info_gain - 0.363 + 特征信息增益为: 特征(有自己的房子)的信息增益最大,选择为根节点特征 + + +## 2.3 图模型 +图模型(Graphical Model)是一种表示概率分布的方法,它将概率分布表示为一个图,图中的节点表示随机变量,节点之间的边表示随机变量之间的依赖关系。 + +### 2.3.1 有向图模型 directed graphical model + +有向图模型是指图中的边是有向的,即边的方向表示了随机变量之间的依赖关系。有向图模型可以表示条件概率分布(CPD),也可以表示马尔科夫随机场。 + +有向图的代表是贝叶斯网。 +贝叶斯⽹与朴素贝叶斯模型建⽴在相同的直观假设上:通过利用分布的条件独立性来获得紧凑而自然的表示。贝叶斯⽹核⼼是⼀个有向⽆环图(DAG),其节点为论域中的随机变量,节点间的有向箭头表⽰这两个节点的依赖关系。 + +![有向图示例](/images/posts/2022-12-16-机器学习入门2_概率论/有向图示例.jpg) + +有向⽆环图可以由如下 3 种元结构构成: +* 同⽗结构 +* V 型结构 +* 顺序结构 + + +贝叶斯网的独立性 +* 局部独立性 +* 全局独立性 + * tail-to-tail + * head-to-tail + * head-to-head + 考虑复杂的有向无环图,如果A,B,C是三个集合,可以是单独的节点或者节点的集合。为了判断A和B是否时C条件独立的,我们可以考虑A和B之间的所有路径。对于其中的一条路径,如果满足以下两点的任意一点,那么就说这条路径是阻塞(blocked)的: + 1. 路径中存在某个节点 X 是 head-to-tail 或者 tail-to-tail 节点,并且 X 是包含在 C 中的; + 2. 路径中存在某个节点 X 是 head-to-head 节点,并且 X 或 X 的⼉⼦是不包含在 C 中的。 + + 如果 A,B 间所有的路径都是阻塞的,那么 A,B 就是关于 C 条件独⽴的;否则 A,B 不是关于 C 条件独⽴的。 + + + + + + + +```python +import networkx as nx +from pgmpy.models import BayesianModel +from pgmpy.factors.discrete import TabularCPD +import matplotlib.pyplot as plt +%matplotlib inline + +# 建立一个简单贝叶斯模型框架 +model = BayesianModel([('a', 'b'), ('a', 'c'), ('b', 'c'), ('b', 'd'), ('c', 'e')]) +# 最顶层的父节点的概率分布表 +cpd_a = TabularCPD(variable='a', variable_card=2, values=[[0.6, 0.4]]) # a: (0,1) + +# 其它各节点的条件概率分布表(行对应当前节点索引,列对应父节点索引) +cpd_b = TabularCPD(variable='b', variable_card=2, # b: (0,1) +values=[[0.75, 0.1], +[0.25, 0.9]], +evidence=['a'], +evidence_card=[2]) +cpd_c = TabularCPD(variable='c', variable_card=3, # c: (0,1,2) +values=[[0.3, 0.05, 0.9, 0.5], +[0.4, 0.25, 0.08, 0.3], +[0.3, 0.7, 0.02, 0.2]], +evidence=['a', 'b'], +evidence_card=[2, 2]) +cpd_d = TabularCPD(variable='d', variable_card=2, # d: (0,1) +values=[[0.95, 0.2], +[0.05, 0.8]], +evidence=['b'], +evidence_card=[2]) +cpd_e = TabularCPD(variable='e', variable_card=2, # e: (0,1) +values=[[0.1, 0.4, 0.99], +[0.9, 0.6, 0.01]], +evidence=['c'], +evidence_card=[3]) + +# 将各节点的概率分布表加入网络 +model.add_cpds(cpd_a, cpd_b, cpd_c, cpd_d, cpd_e) +# 验证模型数据的正确性 +print(u"验证模型数据的正确性:",model.check_model()) +# 绘制贝叶斯图 (节点 + 依赖关系) +nx.draw(model, with_labels=True, node_size=1000, font_weight='bold', node_color='y', \ +pos={"e":[4,3],"c":[4,5],"d":[8,5],"a":[2,7],"b":[6,7]}) +plt.text(2,7,model.get_cpds("a"), fontsize=10, color='b') +plt.text(5,6,model.get_cpds("b"), fontsize=10, color='b') +plt.text(1,4,model.get_cpds("c"), fontsize=10, color='b') +plt.text(4.2,2,model.get_cpds("e"), fontsize=10, color='b') +plt.text(7,3.4,model.get_cpds("d"), fontsize=10, color='b') + +plt.show() +``` + + + --------------------------------------------------------------------------- + + ModuleNotFoundError Traceback (most recent call last) + + in + 1 import networkx as nx + ----> 2 from pgmpy.models import BayesianModel + 3 from pgmpy.factors.discrete import TabularCPD + 4 import matplotlib.pyplot as plt + 5 get_ipython().run_line_magic('matplotlib', 'inline') + + + ModuleNotFoundError: No module named 'pgmpy' + + +### 2.3.2 无向图模型 (Undirected Model) +马尔可夫网节点间的依赖关系是无向的(相互平等的关系),⽆法⽤条件概率分布来表⽰,为此为引⼊极大团概念,进⽽为每个极⼤团引⼊⼀个势函数作为因⼦,然后将联合概率分布表⽰成这些因⼦的乘积再归⼀化,归⼀化常数被称作配分函数。 + diff --git "a/_posts/2022-12-20-pytest\346\265\213\350\257\225\345\205\245\351\227\250.md" "b/_posts/2022-12-20-pytest\346\265\213\350\257\225\345\205\245\351\227\250.md" new file mode 100644 index 0000000000..d03548af4f --- /dev/null +++ "b/_posts/2022-12-20-pytest\346\265\213\350\257\225\345\205\245\351\227\250.md" @@ -0,0 +1,309 @@ +--- +layout: post +title: "pytest测试入门" +date: 2022-12-20 +description: "介绍pytest的基本使用方法," +tag: 自动化测试 +--- + +## 1. pytest简介 +**Pytest** 是一个比较成熟且功能完备的 Python 测试框架。其提供完善的在线文档,并有着大量的第三方插件和内置帮助,适用于许多小型或大型项目。Pytest 灵活易学,打印调试和测试执行期间可以捕获标准输出,适合简单的单元测试到复杂的功能测试。还可以执行 nose, unittest 和 doctest 风格的测试用例,甚至 Django 和 trial。支持良好的集成实践, 支持扩展的 xUnit 风格 setup,支持非 python 测试。支持生成测试覆盖率报告,支持 PEP8 兼容的编码风格。 + +## 2. pytest安装 +```python +pip install pytest +``` + +## 3. pytest基本使用 +### 3.1 pytest命令行参数 +```python +py.test [options] [file_or_dir] [file_or_dir] [...] +``` +#### 3.1.1 常用参数 +```python +py.test -h,--help # 查看命令行和配置文件帮助 +pytest --markers # 查看所有的标记标记 +``` + +```shell +-l,--showlocals #在traceback中显示本地变量 +-q,--quiet #减少输出 +--collect-only:只收集测试用例,不执行测试用例 +-v,--verbose #详细输出 +# 调试输出 +-x:遇到用例失败后,停止执行后面的用例 +--maxfail=num:最多允许num个测试用例失败,后面的用例不再执行 +--pdb:当测试用例失败时,进入调试模式 +--tb=style:指定traceback的输出风格 + - long:完整的traceback信息 + - short:只显示每个失败用例的第一行信息 + - no:不显示traceback信息 + - native:使用C语言的格式化1输出traceback信息 +-m MARKEXPR:只运行标记了指定标记的用例 +``` + +### 3.2 执行选择用例 + +```python +# 执行单个模块中的全部用例 +pytest test_module.py +# 执行单个类中的全部用例 +pytest test_module.py::TestClass +# 执行单个方法 +pytest test_module.py::TestClass::test_method +# 执行指定module中的某个测试函数 +pytest test_module.py::test_func + +# 执行指定路径下的全部用例 +pytest test_dir/ +# 执行字符串匹配的用例 +pytest -k "string_expression" +# 执行标记为smoke的用例 +pytest -m smoke +# 执行标记为smoke且用例名中包含add的用例 +pytest -k "add and smoke" +# 执行标记为smoke或用例名中包含add的用例 +pytest -k "add or smoke" +# 导入模块,使用其文件系统中的路径来查找和执行用例,执行pkg包中的全部用例 +pytest --pyargs pkg # pyargs参数,pkg是包名 +``` +### 3.3 断言 + +通常情况下使用 **assert** 语句就能对大多数测试进行断言。对于异常断言,可以使用上下文管理器 **pytest.raises**: + +```python +# 断言异常,可以使用上下文管理器**pytest.raises** +def test_divide_by_zero(): + with pytest.raises(ZeroDivisionError): + 1 / 0 # 在这里抛出异常,断言成功 +# 还可以使用pytest.raises的context manager的value属性来获取异常信息 +def test_divide_by_zero(): + with pytest.raises(ZeroDivisionError) as excinfo: #excinfo是异常信息 + 1 / 0 + assert 'division by zero' in str(excinfo.value) +# 捕捉异常信息还包括 +def test_divide_by_zero(): + with pytest.raises(ZeroDivisionError,message=" integer division or modulo by zero"): + 1 / 0 +# 对于警告断言,可以使用pytest.warns(上下文管理器): +# UserWarning是警告类型,match是匹配的字符串 +with pytest.warns(UserWarning,match='must be 0 or None'): + warnings.warn("my warning", UserWarning) +with pytest.warns(RuntimeWarning): + warnings.warn("my warning", RuntimeWarning) +``` +对于自定义的assert比较断言,还可以通过在**conftest.py**中定义**pytest_assertrepr_compare**函数来实现: + +```python + +# content of test_foocompare.py +class Foo: + def __init__(self, val): + self.val = val + + def __eq__(self, other): + return self.val == other.val + +def test(): + assert 1 == 1 + +def test_compare(): + f1 = Foo(1) + f2 = Foo(2) + f3 = Foo(1) + assert f1 == f3 + assert f1 == f2 + + +# content of conftest.py +def pytest_assertrepr_compare(op, left, right): + from test_foocompare import Foo + if isinstance(left, Foo) and isinstance(right, Foo) and op == "==": + return ['Comparing Foo instances:', 'vals: %s != %s' % (left.val, right.val)] +``` + +如果需要手动设置断言失败的信息,可以使用**pytest.fail**函数: + +```python +def test_fail(): + pytest.fail("manual fail") + +def test_sys_version(): + # skip test if not running on Python 3.x + if sys.version_info < (3, 0): + pytest.fail("requires python3") +``` + +### 3.4 跳过用例 + +```python +# 跳过用例 +@pytest.mark.skip(reason="no way of currently testing this") +def test_function(): + pass +# 跳过用例,如果条件为真 +@pytest.mark.skipif(sys.version_info < (3, 0), reason="requires python3") +def test_function(): + pass +# 跳过用例,如果条件为假 +@pytest.mark.skipif(sys.version_info >= (3, 0), reason="requires python3") +def test_function(): + pass +``` + +使用 **pytest.skip** 和 **pytest.xfail** 能够实现跳过测试的功能,skip 表示直接跳过测试,而 xfail 则表示存在预期的失败,但两者的效果差不多: + +```python +def test_skip_and_xfail(): + if sys.version_info[0] < 3: + pytest.skip("requires python3") + else: + pytest.xfail("xfail on python3") +``` + +**pytest.importorskip** 函数可以用来跳过测试,如果导入模块失败: + +```python +# 跳过用例,如果导入模块失败 +def test_importorskip(): + pytest.importorskip("this_module_does_not_exist") +# 要求导入的模块的版本号大于等于指定的版本号 +def test_importorskip(): + pytest.importorskip("this_module_does_not_exist", minversion="1.0") +``` +断言近似相等时,可以使用**pytest.approx**函数: + +```python +def test_approx(): + assert 0.1 + 0.2 == pytest.approx(0.3) +def test_approx2(): + assert 2.2==pytest.approx(2.3,rel=0.1) # rel是相对误差 +``` + +### 3.5 conftest.py +从广义理解,**conftest.py**是一个配置文件,可以用来配置pytest的运行环境,从狭义理解,**conftest.py**是一个pytest的插件,可以用来定义pytest的fixture、hook函数、命令行参数等。**conftest.py**文件可以放在测试用例所在的目录下,也可以放在测试用例所在目录的上一级目录下,如果放在上一级目录下,那么该目录下的所有子目录都会加载该**conftest.py**文件。 + +**conftest.py**文件功能如下: +* 定义fixture,用于给测试用例提供静态的测试数据,其可以被所有的测试用于访问,除非指定了范围。 +* **加载插件**,用于导入外部插件或者模块: + ```python + import pytest + pytest_plugins = "myapp.testsupport.myplugin" + ``` +* 定义钩子:用于定义pytest的钩子函数,比如**pytest_runtest_setup**、**pytest_configure**、**pytest_runtest_teardown**等。 + +```python +# content of conftest.py +import pytest + +def pytest_addoption(parser): + parser.addoption("--full", action="store_ture", + help="run full test") + +# content of test.py +@pytest.mark.skipif(not pytest.config.getoption("--runslow")) +def test_func_slow_1(): + """当在命令行执行 --runslow 参数时才执行该测试""" + print 'skip slow' +``` + +### 3.6 Fixture +**Fixture** 是 pytest 特有的功能,它用 pytest.fixture 标识,定义在函数前面。在编写测试函数的时候,可以将此函数名称做为传入参数,pytest 将会以依赖注入方式,将该函数的返回值作为测试函数的传入参数。 + +```python +@pytest.fixture +def login(): + print("登录操作") + username + return username +def test_soso(login): + print(f"搜索{login}") +def test_cart(login): + print(f"添加购物车{login}") +``` + +**fixture 的作用域** fixture 的作用域默认是函数级别的,也就是说,每个测试用例都会执行一次 fixture 函数。如果想要在多个测试用例中使用 fixture 函数的返回值,可以将 fixture 的作用域设置为模块级别. + +* function:默认值,每个测试用例都会执行一次 fixture 函数; +* module: 每个模块加载前执行一次; +* session:每个会话加载前执行一次; + +### 3.7 Markers + +**marker** 是 pytest 特有的功能,它用于给测试用例打标签,可以用于过滤测试用例,也可以用于给测试用例分类。 + +```python +# 跳过测试 +@pytest.mark.skip(reason="no way of currently testing this") +# 满足某个条件时跳过测试 +@pytest.mark.skipif(sys.version_info < (3, 3), reason="requires python3.3+") +# 期望测试失败 +@pytest.mark.xfail(condition, reason, run=True, raises=None, strict=False) +# 参数化测试函数,给测试用例传入参数,供运行时填充到测试中 +@pytest.mark.parametrize(argnames, argvalues, indirect=False, ids=None, scope=None) +# 执行某一类测试用例 +pytest -m smoke +# 测试顺序 +@pytest.mark.run(order=1) +# 让测试尽早地被执行 +@pytest.mark.tryfirst +# 让测试尽晚地被执行 +@pytest.mark.trylast + +``` + +### 3.8 生成测试报告 + +### 3.9 第三方插件 + +pytest-randomly: 测试顺序随机 +
+pytest-ordering: 测试顺序控制 +
+pytest-xdist: 分布式测试 +
+pytest-cov: 生成测试覆盖率报告 +
+pytest-rerunfailures: 失败重试 +
+pytest-timeout: 超时测试,允许在命令行指定超时时间,或者直接在测试代码中标注超时时间。 +
+pytest-repeat: 重复测试(可以使用--count参数指定重复次数) +
+pytest-instafail: 测试失败时立即停止,查看错误的详细信息。 +
+pytest-sugar: 优化测试报告,显示色彩和进度条 +
+pytest-html: 为测试生成HTML报告 +```shell +pytest --html=report.html +``` +
+ +**静态分析用的插件** + +pytest-pycodestyle: + +
+pytest-pep8: 检测代码是否符合 PEP8 规范 +
+pytest-flakes: 检测代码风格 + +
+ +Web开发用的插件: +Web项目有特定的测试逻辑,pytest也不能让测试变简单,但是有几个插件可以帮忙。 +
+pytest-django: 用于测试 Django 项目,Django是很流行的基于Python的web开发框架,它本身包含用于测试的hook函数,允许测试Django应用的各个组件,Django测试使用的是unittest; +
+pytest-selenium:借助浏览器完成自动化测试,比如说启动一个web浏览器,打开网址URL,运行web应用; +
+pytest-flask:测试Flask应用,Flask是一个轻量级的web框架,pytest-flask插件提供了一组很有用的fixture来帮助Flask应用; + + + + + + + + diff --git "a/_posts/2022-12-28-pytest\346\265\213\350\257\225\345\256\236\346\210\230.md" "b/_posts/2022-12-28-pytest\346\265\213\350\257\225\345\256\236\346\210\230.md" new file mode 100644 index 0000000000..5b7cff572a --- /dev/null +++ "b/_posts/2022-12-28-pytest\346\265\213\350\257\225\345\256\236\346\210\230.md" @@ -0,0 +1,32 @@ +--- +layout: post +title: "pytest测试实战" +date: 2022-12-28 +description: "介绍pytest安装Tasks项目用于皮下测试,pytest的测试用例,以及使用多种插件配合进行测试的流程。" +tag: 自动化测试 +--- + +## 软件测试是什么 +软件测试的一个目的是验证你的猜想-猜想软件的内部逻辑,包括第三方的模块、代码包。甚至Python的内建的数据结构是如何运行的。Tasks项目使用名为Task的数据结构,它是用namedtuple工厂函数生成的。Task用于在UI层和API层之间传递信息。 +```python +from collections import namedtuple +Task = namedtuple('Task', ['summary', 'owner', 'done', 'id']) +# 使用_new_.defaults_创建一个默认的Task实例 +Task.__new__.__defaults__ = (None, None, False, None) +``` +运行pytest时可以指定目录和文件。如果不指定,会搜索当前目录以及其子目录中以test_开头或者已_test结尾的文件。pytest会自动搜索这些文件中的测试用例。 +rootdir: D:\pytest\Tasks\tests, +inifile: pytest.ini & tox.ini & setup.cfg + +测试结果 +每个文件的测试情况只占据一行,两个点号表示两个测试用例均已通过;而Failure/erro/skip/xfail/xpass表示测试用例失败/错误/跳过/预期失败/预期失败但通过,会被标记为F/E/s/x/X。 + +## 编写测试函数 +测试函数的命名规则:test_开头或者以_test结尾。 +测试Tasks项目的文件目录如下: +![Task项目文件夹](/images/posts/2022-12-28-pytest测试实战/Task项目文件夹目录.jpg) + +所有的测试都放在tests文件夹中,与包含程序源代码的src文件夹分开。有一些文件比较重要,如README.rst(rst是使用reStrucuredText编写的文档格式,readme是项目须知,CHANGELOG是记录项目变更的日志文件),LICENSE.txt(许可证文件),setup.py(安装脚本),setup.cfg(配置文件),pytest.ini(配置文件),tox.ini(配置文件),conftest.py(本地插件库,hook函数将自定义逻辑引入pytest,用于改善pytest的执行流程;fixtures则是一些用于测试前后执行配置及销毁逻辑的外壳函数)、_init_.py(初始化文件,表示函数主入口)。 + +## 使用fixture执行配置和销毁逻辑 +@pytest.fixture()中的参数scope表示fixture的作用域,有function、class、module、session四个待选值。 diff --git "a/_posts/2023-01-11-Linux\347\275\221\347\273\234\351\205\215\347\275\256.md" "b/_posts/2023-01-11-Linux\347\275\221\347\273\234\351\205\215\347\275\256.md" new file mode 100644 index 0000000000..115b9c599f --- /dev/null +++ "b/_posts/2023-01-11-Linux\347\275\221\347\273\234\351\205\215\347\275\256.md" @@ -0,0 +1,343 @@ +--- +layout: post +title: "Linux网络配置" +date: 2023-01-11 +description: "介绍如何查看Linux系统下的网络配置,查看主机名、网卡、路由器、网络连接方式,测试网络连通等等" +tag: Linux +--- + +# linux系统的网络连接状态 + +## LINUX网络设置 +这一节主要讲如何查看并修改如主机名,路由器,网卡,网络连接方式等等。 + +### 1.查看网络配置 + +查看所有活动的网络接口 + + ifconfig -a + +ens33:第一块以太网卡的名称,"ens33"中的"en"是""EtherNet”"的缩写,表示网卡类型为以太网,"s"表示热插拔插槽上的设备(hot-plugslot.·数字“33”表示插槽编号。 + + ifconfig ens33 + inet 192.168.245.211 #ip地址 + netmask 255. + broadcast 192.168.245.255 #广播地址 + inet6 + ether 00:0c:29:5a:5a:5a #mac地址 + + + +### 2.查看网络状态 + + netstat -tunlp + # -t:查看TCP (Transmission Control Protocol,传输控制协议)相关的信息 + # -u:查看UDP (User Datagram Protocol,用户数据报协议)相关的信息 + # -n:显示数字格式的IP地址(主机地址、端口信息) + # -l:显示监听套接字(Listenin状态的网络连接及端口信息) + # -p:显示进程信息(该选项需要root权限) + + +### 3. 临时修改主机名 + + hostnamectl set-hostname +### 4.永久修改主机名 + + vim /etc/hostname + vim /etc/hosts + +### 5.测试网络连通性 + + ping www.baidu.com + ping -c 3 www.baidu.com #测试3次 + ping -c 3 -i 0.2 www.baidu.com #每次间隔0.2秒 + ping -c 3 -i 0.2 -w 1 www.baidu.com #超时时间为1秒(只ping五秒,5秒之后结束) + +一般来说,网络不通,用ping来定位问题节点的位置,ping的顺序如下: + + ping 127.0.0.1 #ping 本地回环,测试本地网络协议是否正常 + ping 192.168.0.31 #ping 本地IP,测试本地网络接口是否正常 + ping 192.168.0.254 # ping 网关,测试网关是否工作正常 + ping 202.106.0.20 # ping 外部网络,测试服务商网络是否工作正常 + +### 6.查看路由表 + + route -n + # -n:以数字格式显示路由表信息 + # -v:显示详细的路由表信息 + # -e:显示路由表中的错误信息 + # -A:显示所有的路由表信息 + # -g:显示网关信息 + # -C:显示缓存信息 + # -F:显示过滤信息 + # -M:显示多播信息 + # -N:显示网络信息 + # -S:显示套接字信息 + # -T:显示TCP信息 + # -U:显示UDP信息 + # -V:显示版本信息 + # -W:显示网卡信息 + # -X:显示X.25信息 + # -Z:显示SCTP信息 + +### 7.查看DNS服务器 + + cat /etc/resolv.conf + #nameserver + #测试DNS服务器是否可用 + nslookup www.baidu.com #目标主机地址 + # 解析DNS信息收集 + dig www.baidu.com + # 本地主机映射文件 + # etc/hosts 文件中记录着一份主机名与 IP 地址的映射关系表,一般用来保存经常需要访问的主机的信息。 + # 当访问一个未知的域名时,先查找该文件中是否有相应的映射记录,如果找不到再去向DNS 服务器查询 + cat /etc/hosts + # 本地主机名解析文件 + # /etc/nsswitch.conf 文件中记录着本地主机名解析的顺序,一般情况下,本地主机名解析的顺序为: + # hosts > dns > files > nis > nisplus > db > ldap > compat > hesiod > myhostname + +### 8.查看网卡信息 + + ifconfig + # -a:显示所有网卡信息 + # -s:显示网卡的简要信息 + # -d:显示网卡的详细信息 + # -v:显示网卡的详细信息 + # -r:显示路由信息 + # -m:显示多播信息 + # -u:显示UDP信息 + # -t:显示TCP信息 + # -x:显示X.25信息 + # -z:显示SCTP信息 + + +### 9.网络连接方式 + +#### 动态网络dhcp + +##### 网桥方式 +link: https://blog.csdn.net/qq_59323083/article/details/125531699 + + # 1.安装网桥工具 + yum install bridge-utils + # 2.配置网桥 + vim /etc/sysconfig/network-scripts/ifcfg-eth0 + # 添加以下内容 + DEVICE=eth0 + BOOTPROTO=dhcp + ONBOOT=yes + BRIDGE=br0 + # 3.配置网桥 + vim /etc/sysconfig/network-scripts/ifcfg-br0 + # 添加以下内容 + DEVICE=br0 + BOOTPROTO=static + IPADDR= + +##### NAT方式 +打开虚拟网络编辑器,所有数据不要去改动,如下图所示: +![虚拟网络编辑器](https://img-blog.csdnimg.cn/63a12d670cf341a5adc3f3f166b5b6ad.png) +打开虚拟机设置,网络适配器,选择NAT模式,然后点击高级,选择端口转发,添加端口转发规则,如下图所示: +![NAT模式](https://img-blog.csdnimg.cn/e6d0107f1569460f9d208a0317073731.png) +重启网络 + + systemctl restart network + +检查网络连通性 + + ping www.baidu.com + +#### 静态网络 + +##### 网桥模式联网(static) + +通过VMware编辑菜单栏点击虚拟网络编辑器,设置VMnet0,选择桥接模式,外部连接不要选自动,而是选择你的宿主主机的网卡,考虑到一般有有线和无线两块网卡,但这里选择的这块网卡是连接Internet的,比如,这个图中所示,我使用无线网卡连接到Internet,所以选择它,然后“确定”。(如图所示) +![设置VMnet0](https://img-blog.csdnimg.cn/c981678cd39f49dbb3439a7fa1467852.png) +虚拟机 设置 网络适配器 选择桥接模式,如下图所示: +![网桥模式联网](https://img-blog.csdnimg.cn/9989048be247446ab3d5fc5ad65153d9.png) +修改配置文件/etc/sysconfig/network-scripts/ifcfg-ens33 +![修改配置文件](https://img-blog.csdnimg.cn/5ebc7c42fd7b4e25a84e48e4977727d1.png) +重启网络服务 + + systemctl restart network + +##### NAT模式联网(static) + +通过VMware编辑菜单栏点击虚拟网络编辑器,选中VMnet8点击NAT设置: +![选中VMnet8点击NAT设置](https://img-blog.csdnimg.cn/823cd3e5c7b44c498c98c31d6d5b7f8b.png) +不使用本地DHCP服务,这样就得勾消DHCP选项,然后查看DHCP确保未启用。 +![不使用本地DHCP服务](https://img-blog.csdnimg.cn/f4b72500cbfb40c98ce590b1333cb7b2.png) + +第四步,打开NAT设置,记录下图中表示的数据用于修改配置文件: +![NAT设置和配置](https://img-blog.csdnimg.cn/4ff009d460364c5bb0babfcdcbca22b1.png) + +第五步,打开虚拟机设置,配置网络适配器,如下图所示: +![配置网络适配器](https://img-blog.csdnimg.cn/e1690125966b4272b34d8dcc06521ee5.png) +第六步,修改配置文件/etc/sysconfig/network-scripts/ifcfg-ens33,修改的时候必须按第四步的IP网段和网关修改,具体根据你自己的情况决定这个网段,比如,我的网段是192.168.75.0,网关是192.168.75.2,子网掩码是255.255.255.0。 +![修改配置文件](https://img-blog.csdnimg.cn/1660d8524b4c4c87bb0acd1e0de981a9.png) +![常见配置](/images/posts/2023-01-11-Linux网络配置/ifcfg常见配置.jpg) + +第七步,重启服务: + + systemctl restart network + +## 配置DNS + +第一步:修改配置文件 /etc/resolv.conf ,如果你公司有自己的DNS服务器,那么用本公司的,也可以到网上查找免费的DNS服务器,比如: + +中国互联网络中心:1.2.4.8、210.2.4.8、101.226.4.6(电信及移动)、123.125.81.6(联通) +阿里DNS:223.5.5.5、223.6.6.6 +googleDNS:8.8.8.8、8.8.4.4 +openDNS:208.67.222.222208.67.220.220208.67.222.220208.67.220.222 +可以阻挡含有恶意网站的dns +208.67.220.123 + +无劫持 去广告或粗俗网站dns:114.114.114.114 和 114.114.115.115 + +拦截钓鱼病毒木马网站,可增强网银、证券、购物、游戏、隐私信息安全的DNS 114.114.114.119 和 114.114.115.119 + +然后修改配置文件 /etc/resolv.conf,如下表所示: +![修改配置文件](https://img-blog.csdnimg.cn/baec07c796924bd6940e2d93ebeb7e92.png) + +**可最多写3个** + +当你配好resolv.conf文件后,过了一会,发现刚才配置好的DNS又失效了,原来DNS被系统重新覆盖或者被清除!所以你还必须完成以下工作。 + +第二步:修改/etc/NetworkManager/NetworkManager.conf文件 + +![修改NetWorkManager.conf文件](https://img-blog.csdnimg.cn/8395cceb7d674894afae6c237390cb60.png) + +## 网络基本管理--与网络配置相关的常用命令 + +1. 查看网络配置信息 +ifconfig 的功能是管理网络接口,用于查看、配置、启用或禁用网络接口。 + + ifconfig interface [aftype] options | address … + + ![ifconfig 命令的主要参数](/images/posts/2023-01-11-Linux网络配置/ifconfig命令的主要参数.jpg) + +ifconfig命令是Linux下用来配置和显示网络接口信息的命令,它可以显示网络接口的详细信息,包括IP地址、子网掩码、广播地址、MAC地址等。 + + ifconfig + #安装ifconfig命令 + yum -y install net-tools + # 从源头开始安装 + yum -y install net-tools.x86_64 + +ifconfig语法 + + ifconfig [选项] [接口名] + ifconfig [选项] [接口名] [地址] [广播地址] [子网掩码] +网卡控制命令 + + ifconfig eth0 up # 启动网卡 + ifconfig eth0 down # 关闭网卡 + ifconfig eth0 + ifup eth0 # 启动网卡 + ifup ens33 + ifup eth0:0 # 启动虚拟网卡 + ifdown ens33 + +nmcli命令 +该命令可以完成网卡上所有的配置工作,并且可以写入配置文件,重启后依然有效。 + + nmcli device show ens33 # 查看网卡信息 + nmcli device status # 查看网卡状态 + nmcli device disconnect ens33 # 断开网卡 + nmcli device connect ens33 # 连接网卡 + nmcli device modify ens33 ipv4.addresses + nmcli device modify ens33 ipv4.addresses + +检查网卡知否连接网线的命令 + + ethtool ens33 # 查看网卡信息 + ethtool ens33 | grep "Link detected" # 查看网卡是否连接网线 + + +2. ping命令 + + ping [options] hostname/ip + + ![ping命令](/images/posts/2023-01-11-Linux网络配置/ping命令常用参数.jpg) + +3. 检查网络状态(netstat) + + netstat [-veenNcCF] [] -r + + +ens33是我们用于连网的网卡,其功能大多已经了解;下面我们着重介绍一下lo和virbr0的功能。 +### 关于lo + +lo(lo: local loopback的简写)是一个虚拟网络接口。所谓虚拟网络接口,是指不从外界接收和发送数据包,只在系统内部接收和发送数据包,不需要驱动程序。 + +lo接口的作用是为本地不同进程间进行数据交换提供服务。直观地说lo就是回环地址127.0.0.1,用于本机通信,数据经过内核处理后直接返回,不会在任何网络中出现。 + +利用命令ping 127.0.0.1可以检查本地TCP/IP协议以及基本数据接口等是否工作正常。 + +观察本机间不同进程交换数据包的过程,可以使用命令tcpdump -i lo。 + + tpcpdump -i lo + +也可以用WireShark进行抓包,选择lo网卡即可。 + + yum -y install wireshark -gnome # 安装wireshark 带有gnome界面 + +然后再终端输入 wireshark 即可打开wireshark +![wireshark界面](https://img-blog.csdnimg.cn/16296ecb53684def8352ecbd39a1c07e.png) + +### 关于virbr0 + +在虚拟机下安装Linux,这样便会看到vibro这个网络设备,其中的IP地址,子网掩码等都是虚拟机控制程序分配的。 + +virbr0其实就是一个虚拟网桥(virtual bridge),你也可以理解为在虚拟网络中的交换机,当你在VMware Workstation下安装了多个虚拟机时,这些虚拟机之间就是通过这个交换机virbr0互联的,没有virbr0,各虚拟机之间无法通信。尽管你的虚拟机与virbr0的IP地址192.168.122.1不属于同一网段,但ping 192.168.122.1是通的,你不妨试试ping一下。 + + + + +## 时间与网络时间同步 + +使用date命令查看之后,发现时间与本地时间不一致。 +NTP(Network Time Protocol)是网络时间协议,是用来同步网络中各个计算机时间的协议。 + +1. 安装ntpdate + + yum -y install ntpdate + yum install ntp ntp-doc ntpdate # 安装ntp + systemctl start ntpd # 启动ntp服务 + systemcl enable ntpd # 设置开机启动 + +需要提醒的是,每当修改配置文件后,都需要重启NTP 服务。 + +2. 同步时间 + + ntpdate后面可以选择 + + 1.asia.pool.ntp.org、 + 2.asia.pool.ntp.org、 + 3.asia.pool.ntp.org + + 也可以选择其他国家的ntp服务器,比如美国的ntp服务器为 + + 0.us.pool.ntp.org、 + 1.us.pool.ntp.org、 + 2.us.pool.ntp.org、 + 3.us.pool.ntp.org。 + + ntpdate cn.pool.ntp.org + date #系统时间和本地时间一致 +3. 将系统时间写入硬件时间 + + hwclock --systohc + timedatectl + +4. 强制系统时间写入CMOS中防止重启失效 + + hwclock -w #将系统时间写入CMOS中 + hwclock -s # 将CMOS时间写入硬件 + hwclock --show # 查看硬件时间 + hwclock -r # 查看CMOS时间 + + + + + diff --git "a/_posts/2023-01-13-python\344\275\240\346\211\200\344\270\215\347\237\245\351\201\223\347\232\204\344\270\200\344\272\233\346\212\200\345\267\247.md" "b/_posts/2023-01-13-python\344\275\240\346\211\200\344\270\215\347\237\245\351\201\223\347\232\204\344\270\200\344\272\233\346\212\200\345\267\247.md" new file mode 100644 index 0000000000..6f94cfff47 --- /dev/null +++ "b/_posts/2023-01-13-python\344\275\240\346\211\200\344\270\215\347\237\245\351\201\223\347\232\204\344\270\200\344\272\233\346\212\200\345\267\247.md" @@ -0,0 +1,605 @@ +--- +layout: post +title: "python你所不知道的一些技巧:列表推导式,迭代器与生成器,装饰器,类的特殊方法(魔法方法),类的装饰器" +date: 2023-01-13 +description: "主要包括列表推导式,迭代器与生成器,装饰器,类的特殊方法(魔法方法),类的装饰器等等" +tag: Python +--- + +主要包括列表推导式,迭代器与生成器,装饰器,类的特殊方法(魔法方法),类的装饰器等等 + +### 列表推导式 + +```python +square = [] +for i in range(10): + square.append(i**2) +print(square) +``` + + [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] + + + +```python +# 利用lambda表达式重构上述代码 +# map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。 +# list()函数可以将map()返回的Iterator转换为list +Iterator = map(lambda x: x**2, range(10)) +square = list(Iterator) +print(square) + +``` + + [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] + + + +```python +# 使用列表生成式 +square = [x**2 for x in range(10)] +print(square) +``` + + [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] + + + +```python +# 其他类型的列表表达式 +[x+y for x in 'ABC' for y in 'XYZ'] +``` + + + + + ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ'] + + + + +```python +# 列表表达式中使用if语句 +[x for x in range(1, 11) if x % 2 == 0] +``` + + + + + [2, 4, 6, 8, 10] + + + + +```python +# 列表表达式中同时使用if语句和for语句 +[x for x in range(1, 11) if x % 2 == 0 for y in range(1, 11) if y % 3 == 0] + +``` + + + + + [2, 2, 2, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, 10, 10] + + + + +```python +# 列表表达式中同时使用if语句和for语句 +print([(x,y) for x in [1,2,3] for y in [3,1,4] if x != y]) +# 等价于 +combination = [] +for x in [1,2,3]: + for y in [3,1,4]: + if x != y: + combination.append((x,y)) +print(combination) +``` + + [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)] + [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)] + + +### 迭代器与生成器 + + +大多数容器对象都可以使用 for 语句,这种访问风格清晰、简洁又方便。 迭代器的使用非常普遍并使得 Python 成为一个统一的整体。 在幕后,for 语句会在容器对象上调用 iter()。 该函数返回一个定义了 \_\_next__() 方法的迭代器对象,此方法将逐一访问容器中的元素。 当元素用尽时,\_\_next__() 将引发 StopIteration 异常来通知终止 for 循环。 + + +```python +for element in [1, 2, 3, 4, 5]: + print(element) +``` + + 1 + 2 + 3 + 4 + 5 + + + +```python +# 可以使用next()函数来调用__next__()方法的迭代器对象 +s = 'abc' +it = iter(s) +it +print(next(it)) +print(next(it)) +print(next(it)) +print(next(it)) +``` + + a + b + c + + + + --------------------------------------------------------------------------- + + StopIteration Traceback (most recent call last) + + in + 6 print(next(it)) + 7 print(next(it)) + ----> 8 print(next(it)) + + + StopIteration: + + + +```python +# 另外一种调用__next__()方法的方法 +n = iter([1, 2, 3]) +print(n.__next__()) +print(n.__next__()) +print(n.__next__()) + +``` + + 1 + 2 + 3 + + + +```python +# 第三种调用__next__()方法的方法 +n = iter([1, 2, 3]) +print(next(n)) +print(next(n)) +print(next(n)) +print(next(n)) +``` + + 1 + 2 + 3 + + + + --------------------------------------------------------------------------- + + StopIteration Traceback (most recent call last) + + in + 4 print(next(n)) + 5 print(next(n)) + ----> 6 print(next(n)) + + + StopIteration: + + +看过迭代器协议的幕后机制,给你的类添加迭代器行为就很容易了。 定义一个 \_\_iter__() 方法来返回一个带有 \_\_next__() 方法的对象。 如果类已定义了\_\_next__(),则 \_\_iter__() 可以简单地返回 self: + + + +```python +class Reverse: + """Iterator for looping over a sequence backwards.""" + def __init__(self, data): + # 构造函数,初始化对象,传入一个可迭代对象,并将其转换为列表 + self.data = data + self.index = len(data) + + def __iter__(self): # 返回对象本身 + return self + + def __next__(self): # 返回下一个元素 + if self.index == 0: + raise StopIteration + self.index = self.index - 1 + return self.data[self.index] +``` + + +```python +rev = Reverse('spam') +iter(rev) +for char in rev: + print(char) +``` + + m + a + p + s + + +#### 生成器 +生成器 是一个用于创建迭代器的简单而强大的工具。 它们的写法类似于标准的函数,但当它们要返回数据时会使用 yield 语句。 每次在生成器上调用 next() 时,它会从上次离开的位置恢复执行(它会记住上次执行语句时的所有数据值)。 一个显示如何非常容易地创建生成器的示例如下: + + +```python +with open('*.txt') as f: + for line in f: + print(line, end='') +# 用生成器表达式来重构上述代码 +def read_file(): + with open('*.txt') as f: + for line in f: + yield line +``` + + + --------------------------------------------------------------------------- + + OSError Traceback (most recent call last) + + in + ----> 1 with open('*.txt') as f: + 2 for line in f: + 3 print(line, end='') + + + OSError: [Errno 22] Invalid argument: '*.txt' + + + +```python +def reverse(data): + for index in range(len(data)-1, -1, -1): + yield data[index] +for char in reverse('spam'): + print(char) +``` + + m + a + p + s + + +可以用生成器来完成的操作同样可以用前一节所描述的基于类的迭代器来完成。 但生成器的写法更为紧凑,因为它会自动创建 __iter__() 和 __next__() 方法。 + +另一个关键特性在于局部变量和执行状态会在每次调用之间自动保存。 这使得该函数相比使用 self.index 和 self.data 这种实例变量的方式更易编写且更为清晰。 + +除了会自动创建方法和保存程序状态,当生成器终结时,它们还会自动引发 StopIteration。 这些特性结合在一起,使得创建迭代器能与编写常规函数一样容易。 + +### 装饰器 +在一些第三方库,比如说pytest中会使用到装饰器。 +[Python-装饰器(wrapper)](https://blog.csdn.net/Could_tzw/article/details/106732924) + + +```python +# 先理解函数的调用,也就是函数的传入参数可以是一个函数名 +def f1(): + print('f1') +def g(func): + print(func,type(func)) + func() +if __name__ == '__main__': + g(f1) +``` + + + f1 + + + +```python +import logging +# logging.warning('Watch out!') # will print a message to the console +logging.basicConfig(level=logging.DEBUG) +def foo(): + logging.info('执行foo函数') + print('foo') +def bar(): + logging.info('执行bar函数') + print('bar') +if __name__ == '__main__': + foo() + bar() + +``` + + foo + bar + + + +```python +# 每个函数都写一遍logging.info(),这样会导致代码冗余,可以写个函数 +import logging +logging.basicConfig(level=logging.DEBUG) +def log(func): + + logging.info(f'执行{func.__name__}函数') + return func() + +def foo(): + print('foo') + +def bar(): + print('bar') +if __name__ == '__main__': + log(foo) + log(bar) +``` + + foo + bar + + + +```python +# 函数还是太麻烦了,可以在函数里面再写一个函数 +# python装饰器用于拓展原来函数功能的一种函数, +# 这个函数的返回值也是一个函数。 + + +import logging +logging.basicConfig(level=logging.DEBUG) +def use_logging(func): #装饰器函数 + def wrapper(): + logging.info(f'执行{func.__name__}函数') + func() + return wrapper +def foo(): + print('foo') +def bar(): + print('bar') +if __name__ == '__main__': + foo = use_logging(foo) + foo() + bar = use_logging(bar) + bar() +``` + + INFO:root:执行foo函数 + INFO:root:执行bar函数 + + + foo + bar + + + +```python +# 这样每次调用还要声明一下,太麻烦了,可以使用@符号装饰器的知识,这样就不用每次都声明了 +import logging +logging.basicConfig(level=logging.DEBUG) +def use_logging(func): # 装饰器函数,func是被装饰的函数 + def wrapper(): + logging.info(f'执行{func.__name__}函数') + func() + return wrapper +@use_logging # 语法糖 @装饰器函数名 @等价于func = use_logging(func) +# 每次调用函数的时候,都会先调用use_logging函数,然后再调用函数本身,这就是装饰器的作用 +def foo(): + print('foo') +@use_logging +def bar(): + print('bar') +if __name__ == '__main__': + foo() + bar() +``` + + INFO:root:执行foo函数 + INFO:root:执行bar函数 + + + foo + bar + + + +```python +# 装饰器@use_logging里面还可以加参数 +import logging +logging.basicConfig(level=logging.DEBUG) +def use_logging(level='info'): # 默认level级别是info + # 装饰器函数里面再增加一个装饰器 + def decractor(func): + def wrapper(*args,**kwargs): + if level=='debug': + logging.debug(f'执行{func.__name__}函数') + elif level=='info': + logging.info(f'执行{func.__name__}函数') + elif level=='warning': + logging.warning(f'执行{func.__name__}函数') + elif level == 'error': + logging.error(f'执行{func.__name__}函数') + func(*args,**kwargs) + return wrapper + return decractor +@use_logging() +# 每次调用函数的时候,都会先调用use_logging函数,然后再调用函数本身,这就是装饰器的作用 +def foo(): + print('foo') +@use_logging(level = 'debug') +def bar(): + print('bar') +if __name__ == '__main__': + foo() + bar() +``` + + INFO:root:执行foo函数 + DEBUG:root:执行bar函数 + + + foo + bar + + +### 类的特殊方法 魔法方法 +在Python中以两个下划线开头的方法,__init__、__str__、__doc__、__new__等,被称为"魔术方法"(Magic methods)。魔术方法在类或对象的某些事件出发后会自动执行,如果希望根据自己的程序定制自己特殊功能的类,那么就需要对这些方法进行重写。 + + + + + +```python +# 最常见的是在class里面的init构造器 +class Config: + def __init__(self,baseip,port): + print('__init__') + self.baseip = baseip + self.port = port + +# 实例化一个类 +if __name__ == '__main__': + config = Config(baseip='127.0.0.1',port = 3000) + print(config.baseip) + print(config.port) +``` + + __init__ + 127.0.0.1 + 3000 + + + +```python +class People(object): + # 创建对象 + def __new__(cls, *args, **kwargs): + print("触发了构造方法") + ret = super().__new__(cls) # 调用父类的__new__()方法创建对象 + return ret ## 将对象返 + # 实例化对象 + def __init__(self, name, age): + self.name = name + self.age = age + print("初始化方法") + # 删除对象 + # del 对象名或者程序执行结束之后 + def __del__(self): + print("析构方法,删除对象") + + +if __name__ == '__main__': + p1 = People('xiaoming', 16) +``` + + 触发了构造方法 + 初始化方法 + 析构方法,删除对象 + + + +```python +# 斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13 +# 特别指出:第0项是0,第1项是第一个1。从第三项开始,每一项都等于前两项之和。 +class Fib(object): + def __init__(self): + pass + def __call__(self,num): + a,b = 0,1; + self.l=[] + + for i in range (num): + self.l.append(a) + a,b= b,a+b + return self.l + def __str__(self): + return str(self.l) + __rept__=__str__ + +f = Fib() +print(f(10)) +``` + + [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] + + +### 类的装饰器 +[Python小技巧:装饰器(Decorator) +](https://www.bilibili.com/video/BV11s411V7Dt/?spm_id_from=trigger_reload&vd_source=a59f9331c2b80795dda1842d7be401ec) + + +```python + +class Foo: + def __init__(self,func): + self.func = func + def __call__(self,*args,**kwargs): + print('before __call__') + self.func() + print('after __call__') +@Foo +def bar(): + print('bar') + +if __name__ == '__main__': + bar() +``` + + before __call__ + bar + after __call__ + + + +```python +import logging +from functools import wraps + +from enum import Enum +class Level(Enum): + debug = 0 + info = 1 + warn = 2 + error = 3 + + +class UseLogging: + + def __init__(self,level:Level): + self.level = level + logging.basicConfig(level=logging.DEBUG) + + + def __call__(self, func): + @wraps(func) + def decorator(*args, **kwargs): + if self.level == Level.debug: + logging.debug(f'正在执行{func.__name__}') + elif self.level == Level.info: + logging.info(f'正在执行{func.__name__}') + elif self.level == Level.warn: + logging.warning(f'正在执行{func.__name__}') + elif self.level == Level.error: + logging.error(f'正在执行{func.__name__}') + func(*args, **kwargs) + return decorator + +@UseLogging(level=Level.warn) +def bar(name,**kwargs): + print('bar',name,kwargs) + +if __name__ == '__main__': + bar("xiaoming",k = 10) +``` + + WARNING:root:正在执行bar + + + bar xiaoming {'k': 10} + diff --git "a/_posts/2023-01-13-\345\234\250Ubuntu\344\270\212\351\205\215\347\275\256python\347\216\257\345\242\203.md" "b/_posts/2023-01-13-\345\234\250Ubuntu\344\270\212\351\205\215\347\275\256python\347\216\257\345\242\203.md" new file mode 100644 index 0000000000..92088bed6e --- /dev/null +++ "b/_posts/2023-01-13-\345\234\250Ubuntu\344\270\212\351\205\215\347\275\256python\347\216\257\345\242\203.md" @@ -0,0 +1,137 @@ +--- +layout: post +title: "在Ubuntu上配置python环境" +date: 2023-01-13 +description: "主要介绍了在Ubuntu下如何安装Pycharm,python,安装git和配置git环境等内容" +tag: Linux +--- + +## 1.安装Pycharm +Pycharm现在也以snap软件包的形式提供,可以使用命令行的方式进行安装 +```bash +sudo snap install pycharm-community --classic +``` + +## 2.安装python +```bash +sudo apt install python3 +``` +查看python版本 +```bash +python3 --version +``` +## 3.安装git +```bash +sudo apt install git +``` +查看git版本 +```bash +git --version +``` +## 4.配置git环境 +因为Gitee会用到SSH,因此需要在shell里检查是否可以连接到Gitee. +```bash +ssh -T git@gitee.com +``` +如果出现以下内容,说明可以连接,需要进行下一步的配置。 +```bash +Warning: Permanently added ‘***’ (RSA) to the list of known hosts. +Permission denied (publickey). +``` +去注册一个Gitee账号,然后配置邮箱。 + +接着,安装SSH Keys(这一步一定要在~/.ssh目录下进行,否则会报错)。 +```bash +cd ~/.ssh +ls +``` +如果没有id_rsa和id_rsa.pub文件,就需要生成SSH Keys。 +```bash +# 备份并移除已经存在的ssh keys,以防万一 +mkdir backup +mv id_rsa* backup +rm id_rsa* +# 生成ssh keys +ssh-keygen -t rsa -C "***@***.com" +``` +运行的时候会出来输入文件名,就输入id_rsa就可以 + +接着又会提示你输入两次密码(该密码是你push文件的时候要输入的密码,而不是github管理者的密码), + +当然,你也可以不输入密码,直接按回车。那么push的时候就不需要输入密码,直接提交到gitee上了。 + +发现,id_rsa(私钥)和id_rsa.pub(公钥)这两个文件被创建了。 + +最后一步,将公钥添加到Gitee上。 +(1)利用gedit/cat命令,查看id_rsa.pub的内容 +(2)在GitHub中,依次点击Settings -> SSH Keys -> Add SSH Key,将id_rsa.pub文件中的字符串复制进去,注意字符串中没有换行和空格。 +(3) 检查SSH Keys是否添加成功 +```bash +ssh -T git@gitee.com +``` +如果出现以下内容,说明可以连接,需要进行下一步的配置。 +```bash +Hi Chamjeumlam! You’ve successfully authenticated, but GitHub does not provide shell access. +``` + +通过以上的设置之后,就能够通过SSH的方式,直接使用Git命令访问Gitee托管服务器了. + +## 5.配置Pycharm的python +在Pycharm中,选择File -> Settings -> Project -> Project Interpreter -> Add -> Existing environment -> 选择python的安装路径 -> OK +python的安装路径可以通过命令行查看 +```bash +which python3 +``` +## 6.配置Pycharm的git +在Pycharm中,选择File -> Settings -> Version Control -> Git -> Path to Git executable -> 选择git的安装路径 -> OK +git的安装路径可以通过命令行查看 +```bash +which git +``` +## 7.配置Pycharm的gitee +下载安装gitee + 1、在Setting中选择Plugins# + 2、在Marketplace下搜索框中搜索gitee + 3、点击Install进行下载安装 + 4、安装完后点击Restart IDE +登陆码云 + 1、File->Setting->搜索gitee# + 2、点击Add account# + 3、输入账号密码进行登陆 +登陆成功后,会有登陆信息提示. +## 8. 建立远程仓库并提交代码 +在Pycharm中,选择VCS -> Import into Version Control -> Share Project on Gitee -> 输入Gitee的用户名和密码 -> OK + +做完这一步,提示错误:successfully created project on Gitee but initial commit failed. +需要进行设置,告诉gitee是谁在运行和更改代码。 +Git 全局设置: +```bash +git config --global user.name "ChanJeunlam" +git config --global user.email "1120952003@qq.com" +``` +创建 git 仓库: +```shell +mkdir InterAutoTest_W +cd InterAutoTest_W +git init +touch README.md +git add README.md +git commit -m "first commit" +git remote add origin https://gitee.com/chanjeunlam/InterAutoTest_W.git +git push -u origin "master" +``` +已有仓库? +``` +cd existing_git_repo +git remote add origin https://gitee.com/chanjeunlam/InterAutoTest_W.git +git push -u origin "master" +``` + +## 9. Pycharm中其他设置 +更改字体大小: +1、点击顶部菜单栏中的File选项,接着点击Settings选项;2、依次打开Editor、Font选项;3、根据需要设置字体和字号即可。 + + + + + diff --git "a/_posts/2023-01-16-yaml\351\205\215\347\275\256\346\226\207\344\273\266\347\232\204\345\237\272\346\234\254\347\224\250\346\263\225.md" "b/_posts/2023-01-16-yaml\351\205\215\347\275\256\346\226\207\344\273\266\347\232\204\345\237\272\346\234\254\347\224\250\346\263\225.md" new file mode 100644 index 0000000000..9dfb2f8bb7 --- /dev/null +++ "b/_posts/2023-01-16-yaml\351\205\215\347\275\256\346\226\207\344\273\266\347\232\204\345\237\272\346\234\254\347\224\250\346\263\225.md" @@ -0,0 +1,363 @@ +--- +layout: post +title: "yaml配置文件的基本用法" +date: 2023-01-16 +description: "主要包括Yaml文件的语法规则,安装方法,Yaml的编写规则,读取方法,Yaml的封装,以及如何在config文件夹下面简历config.yaml配置文件以实现测试用例和测试数据分离。" +tag: 自动化测试 +--- + +主要包括Yaml文件的语法规则,安装方法,Yaml的编写规则,读取方法,Yaml的封装,以及如何在config文件夹下面简历config.yaml配置文件以实现测试用例和测试数据分离。 + +# 配置文件Yaml +## Yaml 介绍 +Yaml 是一种所有编程语言可用的友好的数据序列化标准。语法和其他高阶语言类似,并且可以简单表达字 +典、列表和其他基本数据类型的形态。例如,我们登录接口地址,就可以写在配置文件中,方便后续调用。 + +语法规则如下: +1. 大小写敏感。 +2. 使用缩进表示层级关系。 +3. 使用空格键缩进,而非Tab键缩进 +4. 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可。 +5. 文件中的字符串不需要使用引号标注,但若字符串包含有特殊字符则需用引号标注; +6. 注释标识为#,注释只能单独占一行。 + +## Yaml的安装 +```bash +pip install pyyaml -i https://mirror.aliyun.com/pypi/simple/ +``` + +## Yaml的编写 +字典格式:键值对的形式,每个键值对占据一行,记得要在:后面加一个空格 +```yaml +name: "test_yml" +result: "success" +``` +列表格式:用-表示列表,后面跟着空格。 +```yaml +- "a" +- "b" +- "c" +``` +输出结果如下: +```commandline +{'name': 'test_yml', 'result': 'success'} +['a', 'b', 'c'] +``` +字典嵌套字典: 下一级的内容要缩进一格再写 +```yaml +person1: + name: xiaoming + age: 18 +person2: + name: xiaohong + age: 20 +``` +输出结果如下: +```commandline +{'person1': {'name': 'xiaoming', 'age': 18}, 'person2': {'name': 'xiaohong', 'age': 20}} +``` +字典嵌套列表:下一级的内容要缩进一格再写 +```yaml +person1: + - xiaoming + - 18 +person2: + - xiaohong + - 20 +``` +输出结果如下: +```commandline +{'person1': ['xiaoming', 18], 'person2': ['xiaohong', 20]} +``` +列表嵌套列表: +```yaml +- + - a + - b + - c +- + - 1 + - 2 + - 3 +``` +输出结果如下: +```commandline +[['a', 'b', 'c'], [1, 2, 3]] +``` +列表嵌套字典: +```yaml +- + name: xiaoming + age: 18 +- + name: xiaohong + age: 20 + ``` +输出结果如下: +```commandline +[{'name': 'xiaoming', 'age': 18}, {'name': 'xiaohong', 'age': 20}] +``` + +## Yaml的读取 +读取单个文件: +```python +import yaml +with open("./data.yaml", "r", encoding="utf-8") as f: # 获取文件对象,f是文件句柄,encoding="utf-8"是指定编码格式,防止出现中文乱码 + # load方法将yaml文件转换成字典或列表 + data = yaml.load(f, Loader=yaml.FullLoader) + # safe_load和load方法一样,但是不支持python的所有数据类型 + print(data) +``` +读取多个文件:这个主要用在比如所多个测试用例的数据都放在一个文件下。 +```python +import yaml +with open("./data.yaml", "r", encoding="utf-8") as f: + # load_all方法将yaml文件转换成生成器对象,每次读取一个文档,每个文档是一个字典或列表. + data = yaml.load_all(f, Loader=yaml.FullLoader) # Loader=yaml.FullLoader是指定加载器,防止出现警告 + for i in data: + print(i) +``` +对应的yaml文件如下: +```yaml +--- +"username1": "test123" +"password": "123456" +--- +"username1": "test456" +"password": "123456" +``` +输出结果如下: +```command +{'username1': 'test123', 'password': '123456'} +{'username1': 'test456', 'password': '123456'} +``` + +## 配置文件-Yaml封装 +yaml封装 → 配置文件conf.yaml → 基本目录配置 → 配置文件读取及使用 +
+在utils文件夹下面新建一个YamlUtil.py文件,用来封装yaml文件的读取操作。 +```python +import yaml +import os +# set up the class to read the yaml file +class YamlReader: + def __init__(self, yamlf): + # 判断文件是否存在 + if os.path.exists(yamlf): + self.yamlf = yamlf + else: + raise FileNotFoundError("文件不存在!") + self._data = None # 定义一个私有属性,用来存储读取的数据 + # @property装饰器的作用是把一个方法变成属性调用 + @property + def data(self): + # read data from yaml file + # 判断是否已经读取过数据,如果已经读取过数据,此时self._data为字典,不为None,就不需要再次读取数据。 + if not self._data: + with open(self.yamlf, 'rb') as f: # rb是以二进制读取 + self._data = yaml.safe_load(f) + return self._data # 返回读取的数据 +``` +测试一下这个util文件是否可以正常读取yaml文件,在t_yaml文件夹下面新建一个yaml_demo.py文件,用来测试YamlUtil.py文件是否可以正常读取yaml文件。 +```python +from utils.YamlUtil import YamlReader +yaml_path = "./data.yml" +data = YamlReader(yaml_path).data +print(data) +``` +yaml文件内容如下: +```yaml +name: "test_yml" +result: "success" +``` +输出结果如下: +```commandline +{'name': 'test_yml', 'result': 'success'} +``` + +以上封装的YamlUtil.py文件只能读取单个yaml文件,如果要读取多个yaml文件,需要在YamlUtil.py文件中添加一个方法,用来读取多个yaml文件。 +
+以上的YamlUtil.py模块在读取下列多个yaml文件时候的报错信息如下: +yaml文件内容: +```yaml +--- +"username1": "test123" +"password": "123456" +--- +"username1": "test456" +"password": "123456" +``` +报错信息如下: +```commandline +raise ComposerError("expected a single document in the stream", +yaml.composer.ComposerError: expected a single document in the stream + in "./data.yaml", line *, column 1 +``` + +在YamlUtil.py文件中添加一个方法,用来读取多个yaml文件: +```python +import yaml +import os +# set up the class to read the yaml file +class YamlReader: + def __init__(self, yamlf): + # 判断文件是否存在 + if os.path.exists(yamlf): + self.yamlf = yamlf + else: + raise FileNotFoundError("文件不存在!") + self._data = None # 定义一个私有属性,用来存储读取的数据 + self._data_all = None # 定义一个私有属性,用来存储读取的数据 + # @property装饰器的作用是把一个方法变成属性调用 + @property + def data(self): + # read data from yaml file + # 判断是否已经读取过数据,如果已经读取过数据,此时self._data为字典,不为None,就不需要再次读取数据。 + if not self._data: + with open(self.yamlf, 'rb') as f: # rb是以二进制读取 + self._data = yaml.safe_load(f) + return self._data # 返回读取的数据 + # 读取多个yaml文件 + def data_all(self): + # read data from yaml file + # 判断是否已经读取过数据,如果已经读取过数据,此时self._data为字典,不为None,就不需要再次读取数据。 + if not self._data_all: + with open(self.yamlf, 'rb') as f: # rb是以二进制读取 + self._data_all = list(yaml.safe_load_all(f)) # list()函数将yaml.safe_load_all(f)转换为列表 + return self._data_all # 返回读取的数据 +``` +yaml文件内容: +```yaml +--- +"username1": "test123" +"password": "123456" +--- +"username1": "test456" +"password": "123456" +``` +yaml_demo.py文件内容: +```python +from utils.YamlUtil import YamlReader +yaml_path = "./data.yaml" +data = YamlReader(yaml_path).data_all() +print(data) +``` +输出结果如下: +```commandline +[{'username1': 'test123', 'password': '123456'}, {'username1': 'test456', 'password': '123456'}] +``` +## Yaml配置文件设置 +在config文件下面,新建一个yaml文件,命名为config.yaml,文件内容如下: +```yaml +# 测试环境 +BASE: + test: + url: "http://211.103.136.242:8064" +``` +在config文件下面,新建一个py文件,命名为config.py,用来读取配置文件config.yaml中的内容,文件内容如下: +首先是,获取当前目录 +```python +# 1. 获取当前基本目录 +import os +current = os.path.abspath(__file__)# __file__表示当前文件,这步操作是获取当前文件的绝对路径 +# /home/hugo/PycharmProjects/InterAutoTest_W/venv/bin/python /home/hugo/PycharmProjects/InterAutoTest_W/config/config.py +tmp_dir = os.path.dirname(current)# 获取当前文件的父目录 +# /home/hugo/PycharmProjects/InterAutoTest_W/venv/bin/python /home/hugo/PycharmProjects/InterAutoTest_W/config +# 获取config目录的父路径 +base_dir = os.path.dirname(tmp_dir) +# /home/hugo/PycharmProjects/InterAutoTest_W/venv/bin/python /home/hugo/PycharmProjects/InterAutoTest_W +# 定义config目录的路径 +_config_dir = os.path.join(base_dir, "config") # _开头的变量表示私有变量 +# /home/hugo/PycharmProjects/InterAutoTest_W/venv/bin/python /home/hugo/PycharmProjects/InterAutoTest_W/config +# 或者也可以这样写: +# _config_dir = base_dir + os.sep + "config" +# 定义config.yaml文件的路径 +_config_yaml = os.path.join(_config_dir, "config.yaml") + +def get_config_path(): + return _config_dir +def get_config_file(): + return _config_yaml +``` + +然后是,读取配置文件config.yaml中的内容 +```python +from utils.YamlUtil import YamlReader +# 1. 获取当前基本目录 +import os +current = os.path.abspath(__file__)# __file__表示当前文件,这步操作是获取当前文件的绝对路径 +# /home/hugo/PycharmProjects/InterAutoTest_W/venv/bin/python /home/hugo/PycharmProjects/InterAutoTest_W/config/config.py +tmp_dir = os.path.dirname(current)# 获取当前文件的父目录 +# /home/hugo/PycharmProjects/InterAutoTest_W/venv/bin/python /home/hugo/PycharmProjects/InterAutoTest_W/config +# 获取config目录的父路径 +base_dir = os.path.dirname(tmp_dir) +# /home/hugo/PycharmProjects/InterAutoTest_W/venv/bin/python /home/hugo/PycharmProjects/InterAutoTest_W +# 定义config目录的路径 +_config_dir = os.path.join(base_dir, "config") # _开头的变量表示私有变量 +# /home/hugo/PycharmProjects/InterAutoTest_W/venv/bin/python /home/hugo/PycharmProjects/InterAutoTest_W/config +# 或者也可以这样写: +# _config_dir = base_dir + os.sep + "config" +# 定义config.yaml文件的路径 +_config_yaml = os.path.join(_config_dir, "config.yaml") + +def get_config_path(): + return _config_dir +def get_config_file(): + return _config_yaml # _下划线开头的变量表示私有变量 + +# 2. 读取配置文件 +class ConfigYaml: + # 初始化yaml读取配置文件 + def __init__(self): + self.config = YamlReader(get_config_file()).data # self.表示当前类的实例 + # 获取url + def get_conf_url(self): + return self.config['BASE']['test']['url'] +if __name__ == '__main__': + conf_read = ConfigYaml() + print(conf_read.get_conf_url()) +``` +这一步做完之后,回到测试用例文件test_mall.py,在这个测试用例,调用config.py文件中的内容,可以实现测试数据和测试用例的分离。 +
+例如,原来的登录测试代码如下: +```python +from utils.RequestsUtils import requests_get, requests_post + +def login(): +# define the testing data + + url = "http://211.103.136.242:8064/autuorizations/" + data ={"username":"python","password":"12345678"} + # # sent the POST request + # response = requests.post(url=url,json=data) + # # print the response + # print(response.json()) + + # 使用utils封装的方法 + response = requests_post(url=url,json=data,headers=None) + # print the response + print(response) +``` + +后来的代码如下: +```python +from utils.RequestsUtils import requests_get, requests_post +from config.config import ConfigYaml + +# 定义测试的url +conf_y = ConfigYaml() +url_path = conf_y.get_conf_url() +## define login in function + +def login(): + # define the testing data + + url = url_path +"/autuorizations/" + # url = "http://211.103.136.242:8064/autuorizations/" + data = {"username": "python", "password": "12345678"} + + # 使用utils封装的方法 + response = requests_post(url=url, json=data, headers=None) + # print the response + print(response) +``` diff --git "a/_posts/2023-01-16-\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\344\271\213requests.md" "b/_posts/2023-01-16-\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\344\271\213requests.md" new file mode 100644 index 0000000000..c39a5990ce --- /dev/null +++ "b/_posts/2023-01-16-\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\344\271\213requests.md" @@ -0,0 +1,217 @@ +--- +layout: post +title: "接口自动化测试之requests" +date: 2023-01-16 +description: "主要包括requests库的安装,接口用例编写,requests库的使用,requests库的返回值介绍,requests库的用例编写以及Requests的封装等等" +tag: 自动化测试 +--- + +主要包括requests库的安装,接口用例编写,requests库的使用,requests库的返回值介绍,requests库的用例编写以及Requests的封装等等。 + + +# 接口用例编写 +实际情况下,每个模块的测试用例写的越详细越好,要根据接口文档进行编写。 + +![png](/images/posts/2023-01-16-接口自动化测试之requests/接口用例编写例子.jpg) + + +# 安装项目依赖 +下载requirements.txt中所需的package: +```commandline +pip install -r requirements.txt +``` +列出已经安装的包裹 +```commandline +pip list +``` + +pip install临时添加镜像源: +```commandline +pip install pytest -i https://pypi.tuna.tsinghua.edu.cn/simple +``` +国内镜像源包括: + +| 镜像源 | 地址 | +| :----- | :----- | +| 清华 | https://pypi.tuna.tsinghua.edu.cn/simple | +| 阿里 | http://mirrors.aliyun.com/pypi/simple/ | +| 豆瓣 | http://pypi.douban.com/simple/ | +| 百度 | https://mirror.baidu.com/pypi/simple | + +# Requests +Requests是一个Python HTTP库,用于发送HTTP请求。它比urllib更加方便,但是它不支持验证、cookies或者其它复杂的HTTP客户端功能。Requests是一个Apache2 Licensed开源项目,由Kenneth Reitz创建和维护。 +## 安装 +```commandline +pip install requests +``` +## 使用 +```python +import requests +r = requests.get('https://api.github.com/events') +print(r.text) +``` +## 请求返回介绍 +``` +r.status_code #响应状态 +r.content #字节方式的响应体,会自动为你解码 gzip 和 deflate 压缩 +r.headers #以字典对象存储服务器响应头,若键不存在则返回None +r.json() #Requests中内置的JSON +r.url # 获取url +r.encoding # 编码格式 +r.cookies # 获取cookie +r.raw #返回原始响应体 +r.text #字符串方式的响应体,会自动根据响应头部的字符编码进行 +r.raise_for_status() #失败请求(非200响应)抛出异常 +``` +## Requests用例编写 + +### 编写登录脚本 +```python +import requests +def login(): + url = "http://172.17.0.139:5004/authorizations/" + data = {"username":"python", + "password":"12345678"} + r= requests.post(url,json=data) + print(r.text) +``` +### 编写个人信息获取脚本 + +```python +import requests +def info(): + url = "http://172.17.0.139:5004/user/" + token = "***" + headers = { + "Authorization": "JWT"+token + } + r = requests.get(url,headers=headers) + print(r.text) +``` + +### 编写商品列表脚本 +```python +import requests +def goods_list(): + url = "http://172.17.0.139:5004/categories/115/skus" + data = { + # "categroy_id":"115", + "page":"1", + "page_size": "10", + "ordering": "create_time",#'create_time', 'price', 'sales' + } + r = requests.get(url, json=data) + print(r.text) + print(r.json) +``` +### 添加到购物车 +```python +import requests +def cart(): + url = "http://172.17.0.139:5004/cart/" + data = { + "sku_id": "3", + "count": "1", + "selected": "true", + } + token = "***" + headers = { + "Authorization": "JWT"+token + } + r = requests.post(url, json=data,headers=headers) + print(r.text) +``` +### 保存订单 +```python +import requests +def order(): + url = "http://172.17.0.139:5004/cart/" + token = "***" + headers = { + "Authorization": "JWT"+token + } + r = requests.get(url,headers=headers) + print(r.text) +``` + +## Requests的封装 +### 封装requests_get方法 +```python +#1、创建封装get方法 +def requests_get(url,headers): + #2、发送requests get请求 + r = requests.get(url,headers = headers) + #3、获取结果相应内容 + code = r.status_code + try: + body = r.json() + except Exception as e: + body = r.text + #4、内容存到字典 + res = dict() + res["code"] = code + res["body"] = body + #5、字典返回 + return res +``` +### requests_post方法封装 +```python +#1、创建post方法 +def requests_post(url,json=None,headers=None): + #2、发送post请求 + r= requests.post(url,json=json,headers=headers) + #3、获取结果内容 + code = r.status_code + try: + body = r.json() + except Exception as e: + body = r.text + #4、内容存到字典 + res = dict() + res["code"] = code + res["body"] = body + #5、字典返回 + return res +``` + +可以发现,requests_get和requests_post方法的区别就是请求方式不同,其他都是一样的,所以我们可以把这两个方法合并成一个方法,这样就可以减少代码量,提高代码的复用性. +```python +# 重构 +# 1、创建类 +class Requests: +# 2、定义公共方法 +# 2.1 增加方法的参数,根据参数来验证请求方式是get还是post +# 2.2 重复的内容,复制进来即可 +# 后续参数还可以加入data、cookies等 + def requests_api(self,url,json=None,headers=None,method="get"): + # 3、判断请求方式 + if method == "get": + r = requests.get(url,headers=headers) + elif method == "post": + r = requests.post(url,json=json,headers=headers) + # 4、获取结果内容 + code = r.status_code + try: + body = r.json() + except Exception as e: + body = r.text + # 5、内容存到字典 + res = dict() + res["code"] = code + res["body"] = body + # 6、字典返回 + return res +### 重构get/post方法 + def get(self,**kwargs): + # 调用公共方法 + return self.requests_api(url,method="get",**kwargs) + def post(self,**kwargs): + # 调用公共方法 + return self.requests_api(url,method="post",**kwargs) +``` + + + + + + diff --git "a/_posts/2023-01-17-Python\344\270\255\345\206\205\345\255\230\345\222\214\345\217\230\351\207\217\347\256\241\347\220\206.md" "b/_posts/2023-01-17-Python\344\270\255\345\206\205\345\255\230\345\222\214\345\217\230\351\207\217\347\256\241\347\220\206.md" new file mode 100644 index 0000000000..c026f775df --- /dev/null +++ "b/_posts/2023-01-17-Python\344\270\255\345\206\205\345\255\230\345\222\214\345\217\230\351\207\217\347\256\241\347\220\206.md" @@ -0,0 +1,211 @@ +--- +layout: post +title: "Python中内存和变量管理" +date: 2023-01-17 +description: "介绍python中引用与对象,可变数据类型与不可变数据类型,值传递,变量和内存管理等方面的内容" +tag: Python +--- + +介绍python中引用与对象,可变数据类型与不可变数据类型,值传递,变量和内存管理等方面的内容 + +## 引用与对象 + +Python 中,一切皆对象。 每个对象由:标识(identity)、类型(type)、value(值) +组成。 +1. 标识用于唯一标识对象,通常对应于对象在计算机内存中的地址。使用内置函数 id(obj)可返回对象 obj 的标识。 +2. 类型用于表示对象存储的“数据”的类型。类型可以限制对象的取值范围以及可执行的 +操作。可以使用 type(obj)获得对象的所属类型。 +3. 值表示对象所存储的数据的信息。使用 print(obj)可以直接打印出值。 + +**对象的本质就是:一个内存块,拥有特定的值,支持特定类型的相关操作.** + +变量也成为:对象的引用。因为,变量存储的就是对象的地址。 +变量通过地址引用了“对象”。 + +简单来说,在 Python中,采用的是基于值的内存管理方式,每个值在内存中只有一份存储。如果给多个变量赋相同的值(如数值类型、字符串类型、元组),那么多个变量存储的都是指向这个值的内存地址,即id。也就是说,Python中的变量并没有存储变量的值,而是存储指向这个值的地址或引用。 + + +![引用与对象](/images/posts/2023-01-17-Python中内存和变量管理/引用与对象.jpg) + +### Python 的赋值机制 + +```python +x = 666 +y = x +print(id(x)) +print(id(y)) + +x = x + 1 +print(id(x)) +print(id(y)) +``` +结果如下: +```console +2015992431536 +2015992431536 +2015992431024 +2015992431536 +``` +执行以上三句,在 python 中意味着什么, 实际发生了什么? + +x = 666会在内存中创建一个整型对象,然后变量x相当于一个标签,贴在此对象上。 +y = x 将y也作为这个整型对象的标签,而不是重新在内存中创建一个对象。 +x = x+1 将 x 的值加 1,然后在内存中创建另一个整型对象667,将x贴在这个对象上,而不是原来的666 上。 + +```python +a=3 +b=3 +c=1 +print("a is b结果:"+str(a is b)) #True +print("a is c结果:"+str(a is c)) #False +print("id(a)=%d,id(b)=%d,id(c)=%d"%(id(a),id(b),id(c))) +``` +运行结果如下: +```console +a is b结果:True +a is c结果:False +id(a)=140730432239456,id(b)=140730432239456,id(c)=140730432239392 +``` +对象保存在内存空间,这里的'a'和'b'是变量,变量是对象的引用,外部想要使用对象的值,需要通过变量来引用对象。同样的值在内存中只有一份存储,所以'a'和'b'引用的对象是相同的,id(a)=140730432239456=id(b),这个对象的引用数量为2。当某个对象的引用数量为0时,对象会被回收。 + +### 变量回收机制 +当我们使用 del 删除变量时,我们到底做了什么? +一句话 : 删除贴在对象上的标签,而不是真的删除内存中的对象。 +那么我们就无法删除内存中的对象,然后节省内存吗? +可以,但是需要通过 python 的垃圾回收机制。简单来说,就是贴在对象上的标签数量为 0 时,会被 python 自动回收。 + +## 可变数据类型与不可变数据类型 + +* 可变数据类型:列表list和字典dict +* 不可变数据类型:整型int、浮点型float、字符串型string和元组tuple + +这里的可变不可变,是指内存中的那块内容(value)是否可以被改变。如果是不可变类型,在对对象本身操作的时候,必须在内存中新申请一块区域(因为老区域不可变)。如果是可变类型,对对象操作的时候,不需要再在其他地方申请内存,只需要在此对象后面连续申请(+/-)即可,也就是它的address会保持不变,但区域会变长或者变短。 + +也就是说: +* python中的不可变数据类型,不允许变量的值发生变化,如果改变了变量的值,相当于是新建了一个对象,而对于相同的值的对象,在内存中则只有一个对象,内部会有一个引用计数来记录有多少个变量引用这个对象; +* 可变数据类型,允许变量的值发生变化,即如果对变量进行append、+=等这种操作后,只是改变了变量的值,而不会新建一个对象,变量引用的对象的地址也不会变化;对于相同的值的不同对象,在内存中则会存在不同的对象,即每个对象都有自己的地址,相当于内存中对于同值的对象保存了多份,这里不存在引用计数,是实实在在的对象。 + +![可变对象](/images/posts/2023-01-17-Python中内存和变量管理/可变对象.png) + +![不可变对象](/images/posts/2023-01-17-Python中内存和变量管理/不可变对象.png) + +## 引用传递与值传递 + +可变对象为引用传递(传址),不可变对象为值传递(传值)。 + +传值和传址的区别 +传值就是传入一个参数的值,传址就是传入一个参数的地址,也就是内存的地址(相当于指针)。他们的**区别是如果函数里面对传入的参数重新赋值,函数外的全局变量是否相应改变**,用**传值传入的参数是不会改变的,用传址传入就会改变**。 + +Python函数参数传递方式:传递对象引用(传值和传址的混合方式),如果是数字,字符串,元组(不可变对象)则传值;如果是列表,字典(可变对象)则传址; + +举个例子: +```python +a=1 +def f(a): + a+=1 +f(a) +print(a) +``` + +结果如下: + +```console +1 +``` + +这段代码里面,因为a是数字类型,所以是传值的方式,a的值并不会变,输出为1 + +```python +a=[1] +def f(a): + a[0]+=1 +f(a) +print(a) +``` + +结果如下: + +```console +[2] +``` + +这段代码里面,因为a的类型是列表,所以是传址的形式,$a[0]$的值会改变,输出为$[2]$. + + + +## 浅拷贝和深拷贝 + +不止是函数里面,函数外面的引用也同样遵循这个规则: + +```python +a=1 +b=a +a=2 +print(a,b) +a=[1] +b=a +a[0]=2 +print(a,b) +``` +输出结果如下: +```console +2 1 +[2] [2] +``` +所以在python中,当运行上面的代码时,如果a是字典或者列表的话,程序执行的操作并不是新建一个b变量,然后a的值复制给b,而是新建一个b变量,把b的值指向a,也就是相当于在c语言里面的新建一个指向a的指针。 +所以当a的值发生改变时,b的值会相应改变。 + +如果想要a的值改变时,b的值不改变,就需要用到浅拷贝和深拷贝。 + +```python +import copy + +a=[1,2,3] +b=a +a.append(4) +print(a,b) + +a=[1,2,3] +b=copy.copy(a) +a.append(4) +print(a,b) +``` +输出结果如下: +```console +[1, 2, 3, 4] [1, 2, 3, 4] +[1, 2, 3, 4] [1, 2, 3] +``` +这里用了copy来让b与a相等,后面如果修改了a的值,b的值并不会改变。看来copy已经可以实现我们上面的提到的需求了,那么deepcopy又有什么用? + +如果我们遇到这种情况,copy就解决不了了: +```python +import copy +a = [1,[1,2],3] +b = copy.copy(a) +a[1].append(3) +print(a,b) +``` +输出结果如下: +```console +[1, [1, 2, 3], 3] [1, [1, 2, 3], 3] +``` +这里a和b的值都改变了,这是因为copy只是复制了一层,如果a里面还有列表,那么b里面的列表也是指向a里面的列表的,所以a里面的列表改变了,b里面的列表也会改变。 +这时候就需要用到deepcopy了: +```python +import copy +a = [1,[1,2],3] +b = copy.deepcopy(a) +a[1].append(3) +print(a,b) +``` +输出结果如下: +```console +[1, [1, 2, 3], 3] [1, [1, 2], 3] +``` +这里a的值改变了,b的值没有改变,这是因为deepcopy是递归的复制了所有的值,所以a里面的列表改变了,b里面的列表不会改变。 + +总结如下: +浅拷贝:浅拷贝中的元素,是对原对象中子对象的引用。此时如果原对象中某一子对象是可变的,改变后会影响拷贝后的对象,存在副作用。一不小心就会触发很大的问题。 + +深拷贝:深拷贝则会递归的拷贝原对象中的每一个子对象,拷贝之后的对象与原对象没有关系。 + diff --git "a/_posts/2023-01-17-bash\350\204\232\346\234\254\346\265\213\350\257\225Ubuntu\346\234\215\345\212\241\345\231\250\347\232\204\346\200\247\350\203\275\345\271\266\350\276\223\345\207\272\346\212\245\345\221\212.md" "b/_posts/2023-01-17-bash\350\204\232\346\234\254\346\265\213\350\257\225Ubuntu\346\234\215\345\212\241\345\231\250\347\232\204\346\200\247\350\203\275\345\271\266\350\276\223\345\207\272\346\212\245\345\221\212.md" new file mode 100644 index 0000000000..c50393bbbb --- /dev/null +++ "b/_posts/2023-01-17-bash\350\204\232\346\234\254\346\265\213\350\257\225Ubuntu\346\234\215\345\212\241\345\231\250\347\232\204\346\200\247\350\203\275\345\271\266\350\276\223\345\207\272\346\212\245\345\221\212.md" @@ -0,0 +1,81 @@ +--- +layout: post +title: "bash脚本测试Ubuntu服务器的性能并输出报告" +date: 2023-01-17 +description: "bash脚本测试Ubuntu服务器的性能并输出报告" +tag: Linux +--- + +bash脚本测试Ubuntu服务器的性能并输出报告 + +```bash + +#!/bin/bash + +# This script is used to test server performance and output report + +# Get the system information +hostname=`hostname` +kernel=`uname -r` + +# Get the CPU information +cpu_model=`cat /proc/cpuinfo | grep "model name" | head -1 | cut -d: -f2` +cpu_cores=`cat /proc/cpuinfo | grep "cpu cores" | head -1 | cut -d: -f2` +cpu_freq=`cat /proc/cpuinfo | grep "cpu MHz" | head -1 | cut -d: -f2` + +# Get the memory information +mem_total=`cat /proc/meminfo | grep "MemTotal" | cut -d: -f2` +swap_total=`cat /proc/meminfo | grep "SwapTotal" | cut -d: -f2` + +# Get the disk information +disk_total=`df -h | grep "/$" | awk '{print $2}'` +disk_used=`df -h | grep "/$" | awk '{print $3}'` + +# Print the report +echo "System Information" +echo "-----------------" +echo "Hostname: $hostname" +echo "Kernel: $kernel" +echo "" +echo "CPU Information" +echo "---------------" +echo "Model: $cpu_model" +echo "Cores: $cpu_cores" +echo "Frequency: $cpu_freq MHz" +echo "" +echo "Memory Information" +echo "-----------------" +echo "Total: $mem_total" +echo "Swap: $swap_total" +echo "" +echo "Disk Information" +echo "----------------" +echo "Total: $disk_total" +echo "Used: $disk_used" +echo "" +``` + +结果如下: +```console +root@hugo-virtual-machine:/opt# sh service.sh +System Information +----------------- +Hostname: hugo-virtual-machine +Kernel: 5.15.0-58-generic + +CPU Information +--------------- +Model: AMD Ryzen 7 4800H with Radeon Graphics +Cores: 2 +Frequency: 2894.461 MHz + +Memory Information +----------------- +Total: 8105812 kB +Swap: 1999868 kB + +Disk Information +---------------- +Total: 23G +Used: 12G +``` \ No newline at end of file diff --git "a/_posts/2023-01-17-python\350\277\233\351\230\266(\344\270\200)_args \345\222\214 kwargs, pdb, Map, Reduce, Filter.md" "b/_posts/2023-01-17-python\350\277\233\351\230\266(\344\270\200)_args \345\222\214 kwargs, pdb, Map, Reduce, Filter.md" new file mode 100644 index 0000000000..6fdf98f960 --- /dev/null +++ "b/_posts/2023-01-17-python\350\277\233\351\230\266(\344\270\200)_args \345\222\214 kwargs, pdb, Map, Reduce, Filter.md" @@ -0,0 +1,247 @@ +--- +layout: post +title: "python进阶(一): *args 和 **kwargs, pdb, Map, Reduce, Filter" +date: 2023-01-17 +description: "主要包括python一些不太常见的知识点以及python高阶编程需要用到的知识点等等" +tag: Python +--- + +主要包括python一些不太常见的知识点以及python高阶编程需要用到的知识点等等。 +比如说*args 和 **kwargs在函数传参以及函数调用中的用法,以及python中的装饰器等等。 +还比如说pdb在程序运行时查看变量的值,查看程序的执行流程,以及查看函数的调用栈。 +还例如,python中的Map、Reduce、Filter、Lambda等在函数式编程中的用法。 + +## *args 和 **kwargs + +其实并不是必须写成\*args 和\**kwargs。 只有变量前面的 \*(星号)才是必须的. 你也可以写成\*var 和\**vars. 而写成\*args 和\**kwargs只是一个通俗的命名约定。 + +\*args 和 \**kwargs 主要用于函数定义。 你可以将不定数量的参数传递给一个函数。 + +预先并不知道, 函数使用者会传递多少个参数给你, 所以在这个场景下使用这两个关键字。 + +**\*args** 是用来发送一个非键值对的可变数量的参数列表给一个函数. + +```python +def test_var_args(f_arg, *argv): + print("first normal arg:", f_arg) + for arg in argv: + print("another arg through *argv:", arg) + +test_var_args('yasoob', 'python', 'eggs', 'test') +``` +这会产生如下输出: +```console +first normal arg: yasoob +another arg through *argv: python +another arg through *argv: eggs +another arg through *argv: test +``` + +**\*\*kwargs** 允许你将不定长度的键值对, 作为参数传递给一个函数. 如果你想要在一个函数里处理带名字的参数, 你应该使用**\*\*kwargs**. + +举个例子: + +```python +def greet_me(**kwargs): + for key, value in kwargs.items(): + print("{0} == {1}".format(key, value)) # .format()是python3的新特性, 用于格式化字符串,{0}表示第一个参数,{1}表示第二个参数 + # 也可以用%来格式化字符串 + print("%s == %s" % (key, value)) %s表示字符串,%d表示整数,%f表示浮点数 +gree_me(name="yasoob") +``` +这会产生如下输出: +```console +name == yasoob +name == yasoob +``` + +### 使用 *args 和 **kwargs 来调用函数 + +那现在我们将看到怎样使用*args和**kwargs 来调用一个函数。 假设,你有这样一个小函数: +```python +def test_args_kwargs(arg1, arg2, arg3): + print("arg1:", arg1) + print("arg2:", arg2) + print("arg3:", arg3) +``` +我们可以使用下面的方式来调用这个函数: +```python +# first with *args +args = ("two", 3, 5) +test_args_kwargs(*args) +# now with **kwargs: +kwargs = {"arg3": 3, "arg2": "two", "arg1": 5} +test_args_kwargs(**kwargs) +``` +这会产生如下输出: +```console +arg1: two +arg2: 3 +arg3: 5 +arg1: 5 +arg2: two +arg3: 3 +``` +### 使用顺序 + +那么如果你想在函数里同时使用所有这三种参数, 顺序是这样的: +```python +some_func(fargs, *args, **kwargs) +``` + +```python +def test_args_kwargs(arg1, arg2, arg3, *args, **kwargs): + print("arg1:", arg1) + print("arg2:", arg2) + print("arg3:", arg3) + for arg in args: + print("another arg:", arg) + for kwarg in kwargs: + print("another keyword arg: {0} = {1}".format(kwarg, kwargs[kwarg])) +``` + +### 什么时候使用它们? +要看你的需求而定,最常见的用例是在写函数装饰器的时候,此外它也可以用来做猴子补丁(monkey patching)。猴子补丁的意思是在程序运行时(runtime)修改某些代码。 + +打个比方,你有一个类,里面有个叫get_info的函数会调用一个API并返回相应的数据。如果我们想测试它,可以把API调用替换成一些测试数据。例如: + +```python +import someclass # someclass是你定义的类 + +def get_info(self, *args): # 这里的self是类的实例,*args是可变参数,可以传入任意多个测试数据 + return "Test data" + +someclass.get_info = get_info +``` + +## 调试(Debugging) + +利用好调试,能大大提高你捕捉代码Bug的。 +Python debugger(pdb)可以帮助我们在程序运行时查看变量的值,查看程序的执行流程,以及查看函数的调用栈。 + +pdb 模块定义了一个交互式源代码调试器,用于 Python 程序。它支持在源码行间设置(有条件的)断点和单步执行,检视堆栈帧,列出源码列表,以及在任何堆栈帧的上下文中运行任意 Python 代码。它还支持事后调试,可以在程序控制下调用。 + +调试器是可扩展的——调试器实际被定义为 Pdb 类。该类目前没有文档,但通过阅读源码很容易理解它。扩展接口使用了 bdb 和 cmd 模块。 + +### pdb +#### 从命令行运行 +将 pdb.py 作为脚本调用,来调试其他脚本。你可以在命令行使用Python debugger运行一个脚本, 举个例子: +```commandline +python -m pdb myscript.py +``` +这会触发debugger在脚本第一行指令处停止执行。这在脚本很短时会很有帮助。你可以通过(Pdb)模式接着查看变量信息,并且逐行调试。 + +#### 从脚本内部运行 + + +同时,你也可以在脚本内部设置断点,这样就可以在某些特定点查看变量信息和各种执行时信息了。这里将使用**pdb.set_trace()**方法来实现。举个例子: +```python +import pdb +def make_bread(): + pdb.set_trace() # 这里设置断点,程序会在这里停止,你可以查看运行到这里时的变量信息,然后按任意键继续执行,或者按c继续执行到下一个断点 + return "I don't have time" +``` +试下保存上面的脚本后运行之。你会在运行时马上进入debugger模式。现在是时候了解下debugger模式下的一些命令了。 + +| 命令 | 简写 | 说明 | +| --- | --- | --- | +| help | h | 显示帮助信息 | +| list | l | 查看当前行的代码段| +| continue | c | 继续执行到下一个断点 | +| quit | q | 退出debugger | +| print | p | 打印变量的值 | +| args | a | 打印函数的参数 | +| break | b | 设置断点 | +| bt | | 打印函数的调用栈 | +| return | r | 打印函数的返回值 | +| next | n | 执行下一行代码 | +| step | s | 进入函数内部 | + +单步跳过(next)和单步进入(step)的区别在于, 单步进入会进入当前行调用的函数内部并停在里面, 而单步跳过会(几乎)全速执行完当前行调用的函数,并停在当前函数的下一行。 + +## Map,Filter 和 Reduce + +Map,Filter 和 Reduce 三个函数能为函数式编程提供便利。我们会通过实例一个一个讨论并理解它们。 + +### Map +Map会将一个函数映射到一个输入列表的所有元素上。这是它的规范: + +```python +map(function_to_apply, list_of_inputs) +``` + +大多数时候,我们要把列表中所有元素一个个地传递给一个函数,并收集输出。比方说: + +```python +items = [1, 2, 3, 4, 5] +squared = [] +for i in items: + squared.append(i**2) +``` + +这里我们使用了一个循环来计算列表中每个元素的平方Map可以让我们用一种简单而漂亮得多的方式来实现。 + +```python +items = [1, 2, 3, 4, 5] +squared = list(map(lambda x: x**2, items)) # map()返回一个迭代器,所以我们需要将它转换为一个列表 +``` + + +不仅用于一列表的输入, 我们甚至可以用于一列表的函数!也即是说,list_of_inputs不仅可以是一个列表,还可以是一个函数列表。比方说: + +```python +def multiply(x): + return (x*x) +def add(x): + return (x+x) +def exponent(x): + return (x**x) # x的x次方 + +funcs = [multiply, add,exponent] # 这里我们有一个函数列表,我们可以将它传递给map, +for i in range(5): + value = list(map(lambda x: x(i), funcs)) # x(i)将会调用funcs中的函数,并传递i作为参数 + print(list(value)) +``` + +输出结果为: +```console +[0, 0, 1] +[1, 2, 1] +[4, 4, 4] +[9, 6, 27] +[16, 8, 256] +``` + +### Filter + +顾名思义,filter过滤列表中的元素,并且返回一个由所有符合要求的元素所构成的列表,符合要求即函数映射到该元素时返回值为True. 这里是一个简短的例子: + +```python +number_list = range(-5, 5) +less_than_zero = filter(lambda x: x < 0, number_list) # lambda x: x < 0是一个函数,它将会映射到number_list中的每个元素上,符合要求的元素将会被保留 +print(list(less_than_zero)) +``` +输出结果为: +```console +[-5, -4, -3, -2, -1] +``` +这个filter类似于一个for循环,但它是一个内置函数,并且更快。 +注意:如果map和filter对你来说看起来并不优雅的话,那么你可以看看列表/字典/元组推导式。 + +### Reduce + +当需要对一个列表进行一些计算并返回结果时,Reduce 是个非常有用的函数。举个例子,当你需要计算一个整数列表的乘积时。 + +通常在 python 中你可能会使用基本的 for 循环来完成这个任务。 + +现在我们来试试 reduce: + +```python +from functools import reduce # python3中reduce被移到了functools模块中 +product = reduce((lambda x, y: x * y), [1, 2, 3, 4]) # reduce()函数将会对列表中的元素进行迭代计算,x是上一次计算的结果,y是下一个元素,这里我们计算的是1*2*3*4,第一次迭代时x=1,y=2,第二次迭代时x=2,y=3,第三次迭代时x=6,y=4,最后保留的结果为24,是x的最终值。 +print(product) +``` +输出结果为: +```console +24 +``` diff --git "a/_posts/2023-01-18-Python\344\270\255\345\207\275\346\225\260\347\232\204\344\275\234\347\224\250\345\237\237.md" "b/_posts/2023-01-18-Python\344\270\255\345\207\275\346\225\260\347\232\204\344\275\234\347\224\250\345\237\237.md" new file mode 100644 index 0000000000..a18671a89a --- /dev/null +++ "b/_posts/2023-01-18-Python\344\270\255\345\207\275\346\225\260\347\232\204\344\275\234\347\224\250\345\237\237.md" @@ -0,0 +1,195 @@ +--- +layout: post +title: "Python中函数的作用域" +date: 2023-01-18 +description: "介绍Python中函数的作用域,局部变量和全局变量等等概念" +tag: Python +--- + + + +回顾一下昨天有关引用传递与值传递有关的内容。 + +## 引用传递与值传递 + +可变对象为引用传递(传址),不可变对象为值传递(传值)。 + +传值和传址的区别 +传值就是传入一个参数的值,传址就是传入一个参数的地址,也就是内存的地址(相当于指针)。他们的**区别是如果函数里面对传入的参数重新赋值,函数外的全局变量是否相应改变**,用**传值传入的参数是不会改变的,用传址传入就会改变**。 + +Python函数参数传递方式:传递对象引用(传值和传址的混合方式),如果是数字,字符串,元组(不可变对象)则传值;如果是列表,字典(可变对象)则传址; + +举个例子: +```python +a=1 +def f(a): + a+=1 +f(a) +print(a) +``` + +结果如下: + +```console +1 +``` + +这段代码里面,因为a是数字类型,所以是传值的方式,a的值并不会变,输出为1 + +```python +a=[1] +def f(a): + a[0]+=1 +f(a) +print(a) +``` + +结果如下: + +```console +[2] +``` + +这段代码里面,因为a的类型是列表,所以是传址的形式,$a[0]$的值会改变,输出为$[2]$. + +我们再打印一下a的地址: +```python +def changelist(a): + a.append(0) + print(id(a)) + print(a) +a = [4,5,6] + +changelist(a) +print("-"*60) +print(id(a)) +print(a) +``` +返回结果为: +```console +2375999356032 +[4, 5, 6, 0] +------------------------------------------------------------ +2375999356032 +[4, 5, 6, 0] +``` +发现在函数内部和函数外部,a的地址是一样的,说明a是传址的,而且在函数内部改变了a的值,函数外部的a的值也改变了。 + + +## 函数作用域 + +所谓作用域(Scope),就是变量的有效范围,就是变量可以在哪个范围以内使用。有些变量可以在整段代码的任意位置使用,有些变量只能在函数内部使用,有些变量只能在 for 循环内部使用。 + +### Python局部变量 + +在函数内部定义的变量,它的作用域也仅限于函数内部,出了函数就不能使用了,我们将这样的变量称为局部变量(Local Variable)。 + +要知道,当函数被执行时,Python 会为其分配一块临时的存储空间,所有在函数内部定义的变量,都会存储在这块空间中。而在函数执行完毕后,这块临时存储空间随即会被释放并回收,该空间中存储的变量自然也就无法再被使用。 + +举例子: +```python +def demo(): + add = "http://c.biancheng.net/python/" + print("函数内部 add =",add) +demo() +print("函数外部 add =",add) +``` + +运行结果如下: +```console +函数内部 add = http://c.biancheng.net/python/ +Traceback (most recent call last): + + File "", line 5, in + print("函数外部 add =",add) + +NameError: name 'add' is not defined +``` + +可以看到,如果试图在函数外部访问其内部定义的变量,Python 解释器会报 NameError 错误,并提示我们没有定义要访问的变量,这也证实了当函数执行完毕后,其内部定义的变量会被销毁并回收。 + + +### Python全局变量 + +除了在函数内部定义变量,Python 还允许在所有函数的外部定义变量,这样的变量称为全局变量(Global Variable)。 + +和局部变量不同,全局变量的默认作用域是整个程序,即全局变量既可以在各个函数的外部使用,也可以在各函数内部使用。 + +定义全局变量的方式有以下 2 种: +1. 在函数体外定义的变量,一定是全局变量,在函数内部也可以使用。 + 1. +2. 在函数体内定义全局变量。即使用 global 关键字对变量进行修饰后,该变量就会变为全局变量。 + + +## 查看变量作用域 + +### globals()函数 +globals() 函数为 Python 的内置函数,它可以返回一个包含全局范围内所有变量的字典,该字典中的每个键值对,**键为变量名,值为该变量的值**。 + +举例子: +```python +#全局变量 +Pyname = "Python教程" +Pyadd = "http://c.biancheng.net/python/" +def text(): + #局部变量 + Shename = "shell教程" + Sheadd= "http://c.biancheng.net/shell/" +print(globals()) +``` +输出结果如下: +```console +{ ...... , 'Pyname': 'Python教程', 'Pyadd': 'http://c.biancheng.net/python/', ......} +``` +这个结果中,我们可以看到,全局变量 Pyname 和 Pyadd 的键值对都被包含在了 globals() 函数返回的字典中。 + +### locals()函数 + +locals() 函数也是 Python 内置函数之一,通过调用该函数,我们可以得到一个包含**当前作用域**内所有变量的字典。这里所谓的“当前作用域”指的是,在函数内部调用 locals() 函数,会获得包含所有局部变量的字典;而在全局范文内调用 locals() 函数,其功能和 globals() 函数相同。 + +```python +#全局变量 +Pyname = "Python教程" +Pyadd = "http://c.biancheng.net/python/" +def text(): + #局部变量 + Shename = "shell教程" + Sheadd= "http://c.biancheng.net/shell/" + print("函数内部的 locals:") + print(locals()) +text() +print("函数外部的 locals:") +print(locals()) +``` +程序执行结果为: +```console +函数内部的 locals: +{'Sheadd': 'http://c.biancheng.net/shell/', 'Shename': 'shell教程'} +函数外部的 locals: +{...... , 'Pyname': 'Python教程', 'Pyadd': 'http://c.biancheng.net/python/', ...... } +``` +当使用 locals() 函数获取所有全局变量时,和 globals() 函数一样,其返回的字典中会默认包含有很多变量,这些都是 Python 主程序内置的,读者暂时不用理会它们。 + +### var(object)函数 + +vars() 函数也是 Python 内置函数,其功能是返回一个指定 object 对象范围内所有变量组成的字典。**如果不传入object 参数,vars() 和 locals() 的作用完全相同**。 + +```python +Pyname = "Python教程" +Pyadd = "http://c.biancheng.net/python/" +class Demo: + name = "Python 教程" + add = "http://c.biancheng.net/python/" +print("有 object:") +print(vars(Demo)) +print("无 object:") +print(vars()) +``` +程序执行结果为: +```console +有 object: +{...... , 'name': 'Python 教程', 'add': 'http://c.biancheng.net/python/', ......} +无 object: +{...... , 'Pyname': 'Python教程', 'Pyadd': 'http://c.biancheng.net/python/', ...... } +``` + diff --git "a/_posts/2023-01-18-Python\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213.md" "b/_posts/2023-01-18-Python\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213.md" new file mode 100644 index 0000000000..73b4416483 --- /dev/null +++ "b/_posts/2023-01-18-Python\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213.md" @@ -0,0 +1,359 @@ +--- +layout: post +title: "Python函数式编程" +date: 2023-01-18 +description: "主要包括Python函数式编程,装饰器等知识点" +tag: Python +--- + +## 高效的函数 + +### 函数是 Python 的头等对象 + +Python 程序中的所有数据都是由对象或对象之间的关系来表示的。①字符串、列表和模块等都是对象。Python 中的函数也不例外,同样是对象。 + +由于 yell 函数是 Python 中的一个对象,因此像任何其他对象一样,也可以将其分配给另一 +个变量: + +```python +def yell(text): + return text.upper()+"!" +bark = yell # 这一行没有调用函数,而是获取 yell 引用的函数对象,再创建一个指向该对象的名称 bark。 +# 现在调用 bark 就可以执行相同的底层函数对象 +bark('hello') # 'HELLO!' +``` + +此时,删除 yell 的引用,不会影响 bark 的引用: + +```python +del yell +yell('hello') # NameError: name 'yell' is not defined +bark('hello') # 'HELLO!' +``` + +Python在创建函数时为每个函数附加了一个用于调试的字符串标识符,使用函数的 __name__ 属性可以访问该标识符: + +```python +bark.__name__ # 'yell' +``` + +虽然函数的__name__仍然是 yell,但已经无法用这个名称在代码中访问函数对象。名称标识符仅仅用来辅助调试,指向函数的**变量**和函数本身实际上是彼此独立的。 + +### 函数可以传递给其他函数 + +由于函数是对象,因此可以将其作为参数传递给其他函数。将函数对象作为参数传递给其他函数的功能非常强大,可以用来将程序中的行为抽象出来并传递出去。 + +```python +def greet(func): + greeting = func("Hi, I am a Python program") + print(greeting) +greet(bark) # 'HI, I AM A PYTHON PROGRAM!' +``` +向 greet 函数传递 bark 函数,greeting = bark("Hi, I am a Python program") = "HI, I AM A PYTHON PROGRAM!",然后打印出来。 + +能接受其他函数作为参数的函数被称为**高阶函数**。高阶函数是函数式编程风格中必不可少的一部分。 + +Python 中具有代表性的高阶函数是内置的 map 函数。map 接受一个函数对象和一个可迭代对象,然后在可迭代对象中的每个元素上调用该函数来生成结果。map 返回一个迭代器,可以使用 list 函数将其转换为列表。 + +```python +list(map(bark,['hello','hey','hi'])) # ['HELLO!', 'HEY!', 'HI!'] +``` + +map 遍历整个列表并将 bark 函数应用于每个元素,然后将结果放入一个列表中。 + +### 函数可以嵌套 + +Python 允许在函数中定义函数,这通常被称为嵌套函数或内部函数。 + +```python +def speak(text): + def whisper(t): + return t.lower()+"..." + return whisper(text) +speak('Hello, World') # 'hello, world...' +``` + +每次调用 speak 时,都会定义一个新的内部函数 whisper 并立即调用。'Hello, World'被传递给text参数,然后whisper函数被调用,返回whisper('Hello, World') = 'hello, world...',最后speak函数返回这个值。 + +内部函数可以访问外部函数的局部变量,但外部函数不能访问内部函数的局部变量。也就是说whisper函数可以访问speak函数的text参数,但speak函数不能访问whisper函数的t参数。 + +```python +whisper('Hello, World') # NameError: name 'whisper' is not defined +``` + +whisper 函数的作用域仅限于 speak 函数,因此在 speak 函数外部无法访问它。 + +那怎么才能从 speak 外部访问嵌套的 whisper 函数呢?由于函数是对象,因此可以将内部函数返回给父函数的调用者. + +例如,下面这个函数定义了两个内部函数。顶层函数根据传递进来的参数向调用者返回对应的内部函数: + +```python +def get_speak_func(text, volume): + def whisper(): + return text.lower()+"..." + def yell(): + return text.upper()+"!" + if volume > 0.5: + return yell + else: + return whisper +get_speak_func('Hello, World', 0.7) # .yell at 0x7f9b8c0b0d08> +get_speak_func('Hello, world',0.3) # .whisper at 0x7f9b8c0b0c80> +``` + +也就是说,get_speak_func 实际上不调用任何内部函数,只是根据 volume 参数选择适当的内部函数,然后返回这个函数对象。 + +如何调用这个函数对象呢?可以像调用普通函数一样调用它: + +```python +speak_func = get_speak_func('Hello, World', 0.7) +speak_func('Hello') # 'HELLO!' +``` + +这意味着函数不仅可以通过参数接受行为,还可以返回行为。这里的 speak_func 函数就是一个行为。 + +### 函数可捕捉局部状态 + +内部函数不仅可以从父函数返回,还可以捕获并携带父函数的某些状态。 + +下面对前面的 get_speak_func 示例做些小改动来逐步说明这一点。新版在内部就会使用 +volume 和 text 参数,因此返回的函数是可以直接调用的: + +```python +def get_speak_func(text, volume): + def whisper(): + return text.lower()+"..." + def yell(): + return text.upper()+"!" + if volume > 0.5: + return yell() + else: + return whisper() + +``` +仔细看看内部函数 whisper 和 yell,注意其中并没有 text 参数。但不知何故,内部函数仍然可以访问在父函数中定义的 text 参数。它们似乎捕捉并“记住”了这个参数的值。 +拥有这种行为的函数被称为**词法闭包(lexical closure)**,简称闭包。闭包在程序流不在闭包范围内的情况下,也能记住封闭作用域(enclosing scope)中的值. + + +词法闭包的一个重要特性是,它们可以捕捉并携带封闭作用域中的状态。这意味着函数不仅可以返回行为,还可以预先配置这些行为。 + +```python +def make_adder(n): + def add(x): + return x + n + return add # 返回函数对象 +plus_3 = make_adder(3) # plus_3 是一个add函数 +plus_3(4) # 7 +plus_5 = make_adder(5) # plus_5 是一个add函数,但是n的值不同 +plus_5(4) # 9 +``` +在这个例子中,make_adder 作为**工厂函数**来创建和配置各种 adder 函数。注意,这些 adder函数仍然可以访问 make_adder 函数中位于封闭作用域中的参数n。 + +### 对象也可以作为函数使用 + +虽然 Python 中的所有函数都是对象,但反之不成立。有些对象不是函数,但依然可以调用,因此在许多情况下可以将其当作函数来对待。 + +如果一个对象是可调用的,意味着可以使用圆括号函数调用语法,甚至可以传入调用参数。 +这些都由__call__双下划线方法完成。 + +```python +class Adder: + def __init__(self, n): + self.n = n + def __call__(self, x): + return self.n + x # self.n 是Adder对象的属性,x是调用时传入的参数 +plus_3 = Adder(3) # plus_3 是一个Adder对象,调用__init__方法 +# 在幕后,像函数那样“调用”一个对象实例实际上是在尝试执行该对象的__call__方法 +plus_3(4) # 7,调用__call__方法,返回self.n + x +``` + +在这个例子中,Adder 类的实例可以像函数一样被调用。这是因为 Adder 类定义了__call__方法,这个方法的实现就是一个函数。当然,并不是所有的对象都可以调用,因此 Python 内置了 callable 函数,用于检查一个对象是否可以调用。 + +### lambda 是单表达式函数 + +你可能想知道 lambda 有什么独特之处:如果只是比用 def 声明函数稍微方便一点,那有什么大不了的? + +来看下面的例子,同时脑海里要记着函数表达式这个概念: + +```python +(lamba x,y:x+y)(5,3)# 8,lambda表达式的结果是一个函数对象,意味着可以使用圆括号函数调用语法,圆括号之后的参数是传入函数的参数 +``` + +从概念上讲,lambda 表达式 lambda x,y:x + y 与用 def 声明函数相同,但从语法上来说表达式位于 lambda 内部。两者的关键区别在于,lambda 不必先将函数对象与名称绑定,只需在 lambda 中创建一个想要执行的表达式,然后像普通函数那样立即调用进行计算。 + +lambda 和普通函数定义之间还有另一个语法差异。lambda 函数只能含有一个表达式,这意味着 lambda 函数不能使用语句或注解(annotation),甚至不能使用返回语句。 +那么应该如何从 lambda 返回值呢?执行 lambda 函数时会计算其中的表达式,然后自动返回表达式的结果,所以其中总是有一个隐式的返回表达式。因此有些人把 lambda 称为**单表达式函数**。 + +#### lambda 的用途 + +从技术上讲,每当需要提供一个函数对象时,就可以使用 lambda 表达式。而且,因为 lambda 是匿名的,所以不需要先分配一个名字. + +比如说,可以用lamba表达式来定义简短的key函数: + +```python +tuples = [(1,'d'),(2,'b'),(3,'c'),(4,'a')] +sorted(tuples,key = lamba x: x[1]) # [(4,'a'),(2,'b'),(3,'c'),(1,'d')] +``` + +上面的例子按照每个元组中的第 2 个值对元组列表进行排序。在这种情况下,用 lambda 函数能快速修改排序顺序。 + +下面是另外一个例子,使用lambda函数快速改变排序顺序: + +```python +sorted(range(-5,6),lamba x: x*x) # [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5] +``` + + +lamba函数的另外应用场景是作为回调函数传递给其他函数。比如,下面的代码使用 lambda 函数来定义一个回调函数,然后将其传递给内置的 filter 函数: + +```python +list(filter(lambda x: x % 2, range(10))) # [1, 3, 5, 7, 9] +# filter函数的第一个参数是一个函数对象,第二个参数是一个可迭代对象 +``` +lambda 还有一个有趣之处:与普通的嵌套函数一样,lambda 也可以像词法闭包那样工作。 + +```python + +def make_repeater(n): + return lambda s: s * n +repeat_5 = make_repeater(5) +repeat_5(3) # 15 +repeat_5('hello') # 'hellohellohellohellohello' +repeat_3 = make_repeater(3) +repeat_3('hello') # 'hellohellohello' +repeat_3(3) # 9 +``` + +#### lambda 的局限性 + +若工作代码用到了 lambda,虽然看起来很“酷”,但实际上对自己和同事都是一种负担。 + +将 lambda 和 map()或 filter()结合起来构建复杂的表达式也很难让人理解,此时用列表解析式或生成器表达式通常会清晰不少. + +```python +# 用lambda和filter构建复杂的表达式 +list(filter(lambda x: x % 2==0, range(16))) # [0, 2, 4, 6, 8, 10, 12, 14] +# 用列表解析式 +[x for x in range(16) if x % 2 == 0] # [0, 2, 4, 6, 8, 10, 12, 14] +``` + +## 装饰器 + +Python 的装饰器可以用来临时扩展和修改可调用对象(函数、方法和类)的行为,同时又不会永久修改可调用对象本身。 + +**装饰器的一大用途是将通用的功能应用到现有的类或函数的行为上**,这些功能包括: + +- 日志记录 +- 访问控制和授权 +- 衡量函数,如执行时间 +- 限制请求速率(rate-limiting ) +- 缓存,等等 +### 装饰器是什么? + +装饰器是用来“装饰”或“包装”另一个函数的,在被包装函数运行之前和之后执行一些代码。 + +那么简单装饰器的实现会是什么样子的呢?用基本术语来说,装饰器是可调用的,将可调用 +对象作为输入并返回另一个可调用对象。 + +下面这个函数就具有这种特性,因此可以认为它是最简单的装饰器: + +```python +def null_decorator(func): + return func +``` + +下面用这个装饰器函数装饰(或包装)另一个函数: + +```python +def greet(): + return 'Hello!' +greet = null_decorator(greet) +greet() # 'Hello!' +``` + +这个例子中定义了一个 greet 函数,然后立即运行 null_decorator 函数来装饰它。这个例子看起来没什么用,因为 null_decorator 是刻意设计的空装饰器。 + +Python中有一个语法糖,可以让装饰器更加简洁,就是使用 @ 符号。下面的代码和上面的代码是等价的: + +```python +@null_decorator +def greet(): + return 'Hello!' +greet() # 'Hello!' +``` + +在函数定义之前放置一个@null_decorator,相当于先定义函数然后运行这个装饰器。 + +注意,使用@语法会在定义时就立即修饰该函数。这样,若想访问未装饰的原函数则需要折 +腾一番。因此如果想保留调用未装饰函数的能力,那么还是要手动装饰需要处理的函数。 + + +### 装饰器可以修改行为 + +在熟悉装饰器语法之后,下面来编写一个有实际作用的装饰器来修改被装饰函数的行为。 + +这个装饰器将被装饰函数返回的结果转换成大写字母: + +```python +def uppercase(func): + def wrapper(): + original_result = func() + modified_result = original_result.upper() # 修改行为, 转换成大写 + return modified_result # 返回修改后的结果 + return wrapper +``` + +这个装饰器的实现中,定义了一个内部函数 wrapper,它将被装饰函数的返回值转换成大写字母。然后,这个内部函数被返回,作为装饰器函数。 + +这个 uppercase 装饰器不像之前那样直接返回输入函数,而是在其中定义一个新函数(闭 +包)。在调用原函数时,新函数会包装原函数来修改其行为。 + +包装闭包(新函数)可以访问未经装饰的输入函数(原函数),并且可在调用输入函数之前 +和之后自由执行额外的代码。 + +来看看加了uppercase装饰器的greet函数,会对原来的greet函数产生什么影响: +```python +@uppercase +def greet(): + return 'Hello!' +greet() # 'HELLO!' +``` + +与 null_decorator 不同,uppercase 装饰器在装饰函数时会返回一个不同的函数对象: + +```python +>>> greet + +>>> null_decorator(greet) + +>>> uppercase(greet) +.wrapper at 0x76da02f28> +``` + +uppercase 定义并返回了另一个函数(闭包),这个函数在后续调用时会运行原输入函数并修改其结果. + + +利用这种特性可以将可重用的代码块(如日志记录和其他功能)应用于现有的函数和类。因 +此装饰器是 Python 中非常强大的功能,在标准库和第三方包中经常用到. + +## 函数式编程 + +函数式编程通过在函数中定义表达式和对表达式求值完成计算。它尽量避免由于状态变化和使用可变对象引入复杂性,让程序变得简洁明了。 + +编程范式并没有统一的划分标准。其中两个范式:函数式编程和命令式编程。 + +在命令式语言(比如 Python)中,计算的状态是通过不同命名空间中变量的值反映的。变量的值决定计算的当前状态,一条语句通过增加或改变(甚至是删除)变量来改变当前状态。“命令式”语言的每一条语句都是一个通过某种方式改变状态的命令。 + +有两种方法可以返回一系列值,而不是生成器表达式。 + +* 编写显式 for 循环:for x in some_iter: yield x +* 使用 yield from 语句:yield from some_iter + +## 来源 + +《Python函数式编程第2版》没看懂 + +《深入理解Python特性》28-40页 + + + diff --git "a/_posts/2023-01-18-python\350\277\233\351\230\266\357\274\210\344\272\214\357\274\211_set(\351\233\206\345\220\210)\357\274\214\344\270\211\345\205\203\350\277\220\347\256\227\347\254\246.md" "b/_posts/2023-01-18-python\350\277\233\351\230\266\357\274\210\344\272\214\357\274\211_set(\351\233\206\345\220\210)\357\274\214\344\270\211\345\205\203\350\277\220\347\256\227\347\254\246.md" new file mode 100644 index 0000000000..e44067d1c2 --- /dev/null +++ "b/_posts/2023-01-18-python\350\277\233\351\230\266\357\274\210\344\272\214\357\274\211_set(\351\233\206\345\220\210)\357\274\214\344\270\211\345\205\203\350\277\220\347\256\227\347\254\246.md" @@ -0,0 +1,112 @@ +--- +layout: post +title: "python进阶(二): set(集合),三元运算符" +date: 2023-01-18 +description: "主要包括python一些不太常见的知识点以及python高阶编程需要用到的知识点等等" +tag: Python +--- + +这一讲主要是介绍了set(集合)数据结构,三元运算符等等内容。 + + +## set(集合)数据结构 + +set(集合)是一个非常有用的数据结构。它与列表(list)的行为类似,区别在于set不能包含重复的值。 +这在很多情况下非常有用。例如你可能想检查列表中是否包含重复的元素,你有两个选择,第一个需要使用for循环,就像这样: + +```python +some_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n'] + +duplicates = [] +for value in some_list: + if some_list.count(value) > 1: + if value not in duplicates: + duplicates.append(value) + +print(duplicates) +``` + +输出结果为: + +```console +['b', 'n'] +``` + +但还有一种更简单更优雅的解决方案,那就是使用集合(sets),你直接这样做: +```python +some_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n'] +duplicates = set([x for x in some_list if some_list.count(x) > 1]) +print(duplicates) +``` + +### 交集 +你可以对比两个集合的交集(两个集合中都有的数据),如下: +```python +valid = set(['yellow', 'red', 'blue', 'green', 'black']) +input_set = set(['red', 'brown']) +print(input_set.intersection(valid)) +``` +输出结果为:{'red'} + +### 差集 + +你可以用差集(difference)找出无效的数据,相当于用一个集合减去另一个集合的数据,例如: + +```python +valid = set(['yellow', 'red', 'blue', 'green', 'black']) +input_set = set(['red', 'brown']) +print(input_set.difference(valid)) +``` +``` +输出结果为:{'brown'} + +### 并集 +你可以用并集(union)来合并两个集合,如下: + +```python +valid = set(['yellow', 'red', 'blue', 'green', 'black']) +input_set = set(['red', 'brown']) +print(input_set.union(valid)) +``` +输出结果为:{'brown', 'red', 'green', 'black', 'blue', 'yellow'} + +## 三元运算符 + + +三元运算符通常在Python里被称为条件表达式,这些表达式基于真(true)/假(not)的条件判断,在Python 2.4以上才有了三元操作。 + +它允许用简单的一行快速判断,而不是使用复杂的多行if语句。 这在大多数时候非常有用,而且可以使代码简单可维护。 + +伪代码如下: + +```console +condition_is_true if condition else condition_is_false +``` +举例子: +```python +is_fat = True +state = "fat" if is_fat else "not fat" +print(state) +``` + +输出结果为:fat + +另一个晦涩一点的用法比较少见,它使用了元组,请继续看: + +伪代码: +```console +#(返回假,返回真)[真或假] +(if_test_is_false, if_test_is_true)[test] +``` +```python +fat = True +fitness = ("skinny", "fat")[fat] +print("Ali is ", fitness) +``` +输出结果为:Ali is fat + +上面的例子没有被广泛使用,而且Python玩家一般不喜欢那样,因为没有Python味儿(Pythonic)。这样的用法很容易把真正的数据与true/false弄混。 + +另外一个不使用元组条件表达式的缘故是因为在元组中会把两个条件都执行,而 if-else 的条件表达式不会这样。 + + diff --git "a/_posts/2023-01-19-\345\256\236\347\224\250\345\260\217\345\267\245\345\205\267.md" "b/_posts/2023-01-19-\345\256\236\347\224\250\345\260\217\345\267\245\345\205\267.md" new file mode 100644 index 0000000000..bc6b6e88d2 --- /dev/null +++ "b/_posts/2023-01-19-\345\256\236\347\224\250\345\260\217\345\267\245\345\205\267.md" @@ -0,0 +1,46 @@ +--- +layout: post +title: "实用小工具: 在线查看github仓库的项目结构,pycharm中好用的插件" +date: 2023-01-19 +description: "介绍最近发现的实用小工具: 在线查看github仓库的项目结构,pycharm中好用的插件" +tag: 工具箱 +--- + +介绍最近发现的实用小工具: 在线查看github仓库的项目结构,pycharm中好用的插件. + + +## 在线查看github仓库的项目结构 + + +方式一:使用gitpod + +Gitpod是一个在线的IDE,可以直接在浏览器中打开github仓库,查看项目结构,编辑代码,运行代码,提交代码等等。 +```console +https://github.com/JuliaGeo/NaturalEarth.jl +``` + +添加前缀gitpod.io/#/: +```console +https://gitpod.io/#/github.dev/JuliaGeo/NaturalEarth.jl +``` + + +方式二:按下句号 + +按下句号,可以在github仓库中查看项目结构,但是只能查看,不能编辑代码。 +举例子: +```console +https://github.com/JuliaGeo/NaturalEarth.jl +``` + +按下句号之后,会进入下面的页面: +```console +https://github.dev/JuliaGeo/NaturalEarth.jl +``` + +这是一个在线的IDE,可以直接在浏览器中打开github仓库,查看项目结构,等等。 + +## pycharm中好用的插件 + +Key Promoter X +Key Promoter X 是用于基于 IntelliJ 产品(如 IDEA,Android Studio 或 CLion)的插件,它有助于在工作时从鼠标操作中 学习基本的键盘快捷键。 当您在 IDE 内部的按钮上 使用鼠标时,Key Promoter X 会显示您应该使用的键盘快捷键(如下图)。 这提供了一种简单的方法来学习如何用键盘按键代替繁琐的鼠标工作,并有助于过渡到更快的 无鼠标开发。 diff --git "a/_posts/2023-01-19-\350\275\257\344\273\266\346\265\213\350\257\225\347\232\204\345\210\206\347\261\273.md" "b/_posts/2023-01-19-\350\275\257\344\273\266\346\265\213\350\257\225\347\232\204\345\210\206\347\261\273.md" new file mode 100644 index 0000000000..2d39c0e4ae --- /dev/null +++ "b/_posts/2023-01-19-\350\275\257\344\273\266\346\265\213\350\257\225\347\232\204\345\210\206\347\261\273.md" @@ -0,0 +1,105 @@ +--- +layout: post +title: "软件测试的分类" +date: 2023-01-19 +description: "介绍软件测试的分类" +tag: 自动化测试 +--- + +## 软件测试分类思维导图 + +![软件测试分类思维导图](/images/posts/2023-01-19-软件测试的分类/软件测试的分类.png) + +## 软件测试分类思维导图-文本模式 +```console +按阶段划分 + 单元测试 + 概念:指对软件中的最小可测单元的检测认证(一般是函数) + 应用场景: 测试某个函数的功能是否正确 + 集成测试 + 概念: 在单元测试的基础上按照设计要求组装成子系统或系统进行测试 + 测试需求: 实践证明,一些模块可以单独工作,但是不能保证集成起来也能正常工作 + 系统测试 + 概念: 系统测试是将经过集成测试的软件和操作系统/硬件看成一个整体,在实际运行环境下测试 + 举例: 百度在浏览器和手机上不同操作系统,都要进行测试 + 验收测试 + 按对象划分 + 产品验收: 基于需求文档的需求验收 + 项目验收: 客户根据需求进行验证性测试 + 按阶段划分 + α测试(内测版本) + β测试(公测版本) + γ测试(待发布版本) + 负责人 + 产品: 产品经理 + 项目:客户(甲方)或第三方测评机构(乙方) + +按代码逻辑划分 + 黑盒测试 + 概念: 把测试对象看成一个不能打开的黑盒子,测试时候,完全不考虑盒子里面的代码逻辑结构和具体运行,只根据需求文档,检测程序功能是否符合文档中的说明,检测输出结果对不对 + 测试依据 + 需求文档 + 重点: 以客户的角度,从输入的数据和输出的数据的对应关系出发进行测试 + 分类 + 功能相关 + 功能测试 + 检查产品是否满足要求 + 界面测试 + UI测试,如元素布局 + 易用性测试 + 用户体验 + 专项测试 + 如安装卸载升级、网络专项 + 性能相关 + 性能测试 + 模拟用户场景,测试系统的各项性能指标,查看是否满足要求 + 压力测试 + 在高负载、资源少的情况下运行测试,找出性能隐患 + 负载测试 + 不断增加负载,测试软件吞吐量上限,以验证系统的负载能力 + 白盒测试 + 概念:把测试对象看成一个打开的透明盒子,测试时候,测试人员利用内部逻辑结构以及相关信息,通过不同分支检查程序状态,检查程序中每条路线能否按照预定要求正确工作 + 测试依据 + 源代码 + 重点 + 必须检查程序的内部结构,从程序的逻辑着手,得出测试的数据(输入和输出的数据) + 对应关系 + 单元测试:白盒 + 集成测试:白盒 + 系统测试:黑盒 + 验收测试:黑盒 + 灰盒测试 + 概念 + 介于黑盒与白盒测试之间,只关注一部分代码逻辑 + +按是否运行代码划分 + 静态测试 + 概念: 静态测试是指不运行被测程序本身,通过分析或检查程序语法/结构/过程来检查程序的正确性 + 测试对象 + 文档 + 需求文档 + 各种设计文档 + 源代码 + 找出程序可能重复的地方 + 找出不安全 + 动态测试 + 概念: 动态测试就是通过运行被测程序,检查运行结果与预期结果的差距,并分析运行效率、健壮性 + 测试对象 + 系统 + 步骤 + 测试用例设计 + 执行测试用例 + 检查运行实际结果与预期结果 + +按是否自动化执行划分 + 手工测试: 手工的方法去执行测试 + 自动化测试:需要借助工具和代码完成手工测试的工作 + +其他 + 回归测试: 回归测试是指修改代码之后,重新进行测试,已确定修改没有引入新的错误 + 冒烟测试: 针对最基本的功能或者流程进行测试 + +来源 + https://blog.csdn.net/karsa__/article/details/108036552 + +``` diff --git "a/_posts/2023-01-28-pytest\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\2251.md" "b/_posts/2023-01-28-pytest\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\2251.md" new file mode 100644 index 0000000000..5e4467880f --- /dev/null +++ "b/_posts/2023-01-28-pytest\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\2251.md" @@ -0,0 +1,373 @@ +--- +layout: post +title: "pytest接口自动化测试1" +date: 2023-01-28 +description: "主要包括pytest安装;pytest创建简单的测试用例;pytest基础使用如函数级别的方法和类级别的方法;pytest常见插件如pytest-html将测试结果以html的形式展示出来,pytest-rerunfailures可以进行测试代码的失败重试;pytest的数据参数化,可以实习同样的测试用例函数对多个输入的测试数据的测试;最后是根据pytest运行原则以及pytest.ini指定测试的文件夹,测试函数等对之前的接口模块函数进行重构,进行自动化测试。" +tag: 自动化测试 +--- + +主要包括pytest安装;pytest创建简单的测试用例;pytest基础使用如函数级别的方法和类级别的方法;pytest常见插件如pytest-html将测试结果以html的形式展示出来,pytest-rerunfailures可以进行测试代码的失败重试;pytest的数据参数化,可以实习同样的测试用例函数对多个输入的测试数据的测试;最后是根据pytest运行原则以及pytest.ini指定测试的文件夹,测试函数等对之前的接口模块函数进行重构,进行自动化测试。 + +## pytest安装 + +在requirements.txt中添加pytest +``` +pytest~=4.5.0 +``` + +在项目根目录下执行 +```bash +pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple +``` + +## pytest使用 + +### 创建简单的测试用例 + +在testcase文件夹下面创建文件夹t_pytest,然后在t_pytest文件夹下创建pytest_demo.py文件,内容如下: + +```python + +import pytest + +#1、创建简单测试方法 +""" +1、创建普通方法 +2、使用pytest断言的方法 +""" +#普通方法 +def func(x): + return x+1 + +#断言的方法 +def test_a(): + print("---test_a----") + assert func(3) == 5 #断言失败 + +def test_b(): + print("---test_b----") + assert 1 #断言 + + +#2、pytest运行 +""" +1、Pycharm代码直接执行 +2、命令行执行 +""" + +#代码直接执行 +if __name__ == "__main__": + pytest.main(["pytest_demo.py"]) +``` + +在Pycharm中直接运行pytest_demo.py文件,还可以增加一些参数: +```python +#代码直接执行 +import pytest +if __name__ == "__main__": + pytest.main(["-s","pytest_demo.py"]) # -s参数可以打印print的内容 +``` +在命令行中执行代码,操作如下: +```bash +# 进入到项目根目录下 +cd testcase/t_pytest +# 执行代码 +python pytest_demo.py +``` +测试结果一个通过,一个失败 + +```console +============================= test session starts ============================== +collecting ... collected 2 items + +pytest_demo.py::test_a FAILED [ 50%]---test_a---- + +testcase/t_pytest/pytest_demo.py:13 (test_a) +4 != 5 + +预期:5 +实际:4 +<点击以查看差异> + +def test_a(): + print("---test_a----") +> assert func(3) == 5 #断言失败 +E assert 4 == 5 +E + where 4 = func(3) + +pytest_demo.py:16: AssertionError + + + + +pytest_demo.py::test_b PASSED [100%]---test_b---- + + +========================= 1 failed, 1 passed in 0.04s ========================== + +进程已结束,退出代码1 +``` + +## pytest的基础使用 + +### 函数级别的方法 + +运行于测试方法的始末,运行一次测试函数会运行一次 setup 和 teardown。 + +在t_pytest文件夹下创建pytest_func.py文件,内容如下: + +```python + +import pytest +""" +1、定义类 +2、创建测试方法test开头 +3、创建setup,teardown +4、运行查看结果 + +""" + +#1、定义类 +class TestFunc: + #3、创建setup,teardown + def setup(self): + print("---setup----") + + def teardown(self): + print("---teardown---") + + #2、创建测试方法test开头 + def test_a(self): + print("test_a") + + def test_b(self): + print("test_b") + +#4、运行查看结果 +if __name__ =="__main__": + pytest.main(["-s","pytest_func.py"]) +``` + +返回控制台结果如下: + +```console +Testing started at 上午10:37 ... +Launching pytest with arguments /home/hugo/PycharmProjects/InterAutoTest_W/testcase/t_pytest/pytest_func.py --no-header --no-summary -q in /home/hugo/PycharmProjects/InterAutoTest_W/testcase/t_pytest + +============================= test session starts ============================== +collecting ... collected 2 items + +pytest_func.py::TestFunc::test_a +pytest_func.py::TestFunc::test_b + +======================== 2 passed, 4 warnings in 0.01s ========================= + +进程已结束,退出代码0 +---setup---- +PASSED [ 50%]test_a +---teardown--- +---setup---- +PASSED [100%]test_b +---teardown--- +``` + +我们发现,这里在运行每个测试方法之前都会运行setup方法,运行完每个测试方法之后都会运行teardown方法。 + +### 类级别方法 + +运行于测试类的始末,在一个测试内只运行一次 setup_class 和 teardown_class,不关心测试类内有多少个测试函数。 + +在t_pytest文件夹下创建pytest_class.py文件,内容如下: + +```python + +import pytest +""" +1、定义类 +2、创建测试方法test开头 +3、创建setup_class,teardown_class +4、运行查看结果 + +""" + +#1定义类 +class TestClass: + #3、创建setup_class,teardown_class + def setup_class(self): + print("---setup_class---") + + def teardown_class(self): + print("---teardown_class---") + + #2、创建测试方法test开头 + def test_a(self): + print("test_a") + + def test_b(self): + print("test_b") + +#4、运行查看结果 +if __name__ =="__main__": + pytest.main(["-s","pytest_class.py"]) +``` + +返回信息如下: + +```console +============================= test session starts ============================== +collecting ... collected 2 items + +pytest_class.py::TestClass::test_a ---setup_class--- +PASSED [ 50%]test_a + +pytest_class.py::TestClass::test_b PASSED [100%]test_b +---teardown_class--- + + +============================== 2 passed in 0.01s =============================== + +进程已结束,退出代码0 +``` + +我们发现,无论执行多少个测试方法,setup_class和teardown_class方法只会执行一次。 + +## pytest常用插件 + +### pytest-html + +测试报告插件,可以将测试结果以html的形式展示出来。 + +安装pytest-html插件,在requirements.txt文件中添加pytest-html,然后在终端中运行pip install -r requirements.txt安装插件。 + +使用,在配置文件pytest.ini中的命令行参数中增加 --html=用户路径/report.html + +```console +[pytest] +addopts = -s --html=../../report/report.html +``` + +执行之前写好的pytest_demo.py文件,查看生成的测试报告。./../report是回到根目录下,创建一个report文件夹,report.html是生成的测试报告。 + +### 失败重试 + +自动化测试脚本可能会使用到网络,如果网络不好可能最终会使脚本不通过。像这种情况可能并不是脚本本身的问题,仅仅是因为网络忽快忽慢,那么我们可以使用失败重试的插件,当失败后尝试再次运行。一般情况最终成功可以视为成功,但最好进行进行排查时候是脚本问题。 + +安装方法:在requirements.txt文件中添加pytest-rerunfailures,然后在终端中运行pip install -r requirements.txt安装插件。 + +使用方法:在配置文件pytest.ini中的命令行参数中增加 --reruns=3,表示失败之后重试3次。重试时,如果脚本通过,那么后续不再重试。 + +```console +[pytest] +addopts =-s --html=../../report/report.html --reruns 3 +``` + +也可以加reruns-delay参数,表示失败之后重试的间隔时间。 + +```console +[pytest] +addopts =-s --html=../../report/report.html --reruns 3 --reruns-delay 2 +``` + +除了在pytest.ini中配置,也可以添加mark标记,在测试用例中添加@mark.flaky(reruns=3,reruns_delay=2),可以只对某个测试用例进行失败重试。 +@mark是装饰器,flaky表示标记。 + +## 数据参数化 + +应用场景 + +登录功能都是输入用户名,输入密码,点击登录。但登录的用户名和密码如果想测试多个值是没有办法用普通的操作实现的。数据参数化可以帮我实现这样的效果。 + +方法名 +pytest.mark.parametrize(argnames,argvalues,indirect=False,ids=None,scope=None) + +argnames:参数名 + +argvalues:参数对应值,类型必须为可迭代类型,一般使用list + +indirect:是否对参数值进行间接调用,如果为True,那么参数值必须是函数名,否则报错; + +ids:参数值对应的id,如果不指定,那么默认使用参数值,如果指定,那么参数值必须和ids的长度一致,否则报错; + +scope:参数值的作用域,可以是function、class、module、session,如果不指定,那么默认为function. + +以下是传入单个参数的例子: + +```python +import pytest + +""" +1、创建类和测试方法 +2、创建数据 +3、创建参数化 +4、运行 +""" +#1、创建类和测试方法 +class TestDemo: + # 2、创建测试数据 + data_list = ["xiaoming","xiaohong"] + + # 3、参数化 + @pytest.mark.parametrize("name",data_list) + + # 1、创建类和测试方法 + def test_a(self,name): + print("test_a") + print(name) + assert 1 + +if __name__ == "__main__": + pytest.main(["pytest_one.py"]) +``` + +以下是多个参数的例子:多个参数是以元祖的方式传入的,元祖中的每个元素对应一个参数。 +```console +@pytest.mark.parametrize((‘param1’,’param2’),[(value1[0],value1[1]),(value2[0],value2[1])]) +``` + +```python +import pytest + +""" +1、创建类和测试方法 +2、创建数据 +3、创建参数化 +4、运行 +""" +#1、创建类和测试方法 +class TestDemo: + #2、创建测试数据 + data_list = [("xiaoming","123456"),("xiaohong","456789")] + + #3、参数化 + @pytest.mark.parametrize(("name","password"),data_list) + def test_a(self,name,password): + print("test_a") + print(name,password) + assert 1 + +if __name__ == "__main__": + pytest.main(["pytest_two.py"]) +``` + + +## 利用pytest来进行接口自动化测试 + +pytest运行原则:在不指定运行目录,运行文件,运行函数等参数的默认情况下,pytest会执行当前目录下所有以test为前缀(test*.py)或者以_test结尾(*_test.py)的文件中以test开头的测试方法。 + +因此,我们需要根据默认运行原则,调整py文件命名,函数命名(比如所之前的login():改成test_login():,也就算重构我们之前的五个接口模块。 然后,pytest.main()运行,或者命令行直接pytest运行。 + +另外,我们还可以通过pytest.ini文件来配置运行参数,比如指定运行文件,运行函数等。 + +pytest.ini文件内容如下: + +```python +[pytest] +# 指定运行路径 +testpaths = ./testcase +# 指定执行目录下的文件 +python_files = test_*.py +# 指定执行测试类 +python_classes = Test* +# 指定执行测试方法 +python_functions = test_* +``` diff --git "a/_posts/2023-01-28-pytest\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\2252-\346\226\255\350\250\200.md" "b/_posts/2023-01-28-pytest\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\2252-\346\226\255\350\250\200.md" new file mode 100644 index 0000000000..a4c69cf687 --- /dev/null +++ "b/_posts/2023-01-28-pytest\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\2252-\346\226\255\350\250\200.md" @@ -0,0 +1,395 @@ +--- +layout: post +title: "pytest接口自动化测试2-断言" +date: 2023-01-28 +description: "断言是一种判断程序执行结果是否符合预期的方法,可以帮助我们定位错误原因,是自动化测试的灵魂。本文主要介绍pytest中使用assert进行断言,断言的应用和封装。断言结果的验证,包括状态码、返回信息验证,以及数据库断言结果验证。" +tag: 自动化测试 +--- + +断言是一种判断程序执行结果是否符合预期的方法,可以帮助我们定位错误原因,是自动化测试的灵魂。本文主要介绍pytest中使用assert进行断言,断言的应用和封装。断言结果的验证,包括状态码、返回信息验证,以及数据库断言结果验证。 + + +# 结果断言 + +## 断言介绍 + +* 断言是一种判断程序执行结果是否符合预期的方法。断言的好处是当测试用例执行失败时,可以很方便地定位错误原因。 +* 断言是自动化最终的目的,一个用例没有断言,就失去了自动化测试的意义了。 +* 断言用到的是 assert关键字。预期的结果和实际结果做对比,符合预期就是pass,不符合就fail。 + +## 常用断言 + +pytest里面的断言就是python里assert的断言方法: + +* assert expression1, expression2 # 断言表达式1为真,否则抛出异常expression2,expression2可以省略,省略时默认抛出AssertionError异常,expression2可以是字符串,也可以是异常对象。 +* assert not expression1 # 断言表达式1为假,否则抛出AssertionError异常。 +* assert expression1 == expression2 # 断言表达式1和表达式2相等,否则抛出AssertionError异常。 +* assert expression1 != expression2 # 断言表达式1和表达式2不相等,否则抛出AssertionError异常。 +* assert expression1 in expression2 # 断言表达式1是表达式2的子集,否则抛出AssertionError异常。 +* assert expression1 not in expression2 # 断言表达式1不是表达式2的子集,否则抛出AssertionError异常。 + +```python +import pytest + +#判断xx为真 +#1、定义方法进行assert +def test_1(): + a = True + assert a + +#判断XX不为真 +def test_2(): + a= True + assert not a + +#判断b包含a +def test_3(): + a="hello" + b="hello world" + assert a in b +#判断a==b +def test_4(): + a=b="hello" + assert a ==b + +#判断a!=b +def test_5(): + a="hello" + b="hello world" + assert a !=b + +#2、运行查看结果 +if __name__ == "__main__": + pytest.main(["pytest_assert.py"]) +``` + +测试结果如下: +```console +============================= test session starts ============================== +collecting ... collected 5 items + +pytest_assert.py::test_1 PASSED +pytest_assert.py::test_2 FAILED +testcase/t_pytest/pytest_assert.py:9 (test_2) +def test_2(): + a= True +> assert not a +E assert not True + +pytest_assert.py:12: AssertionError + + + + +pytest_assert.py::test_3 PASSED +pytest_assert.py::test_4 PASSED +pytest_assert.py::test_5 PASSED + +========================= 1 failed, 4 passed in 0.06s ========================== + +进程已结束,退出代码1 + +``` +## 断言应用和封装 + +### 断言应用 +首先,在utils目录下新建一个AssertUtil.py文件,用来封装断言方法。 + +```python +from utils.LogUtil import my_log +import json +#1、定义封装类 +class AssertUtil: +#2、初始化数据,日志 + def __init__(self): + self.log = my_log("AssertUtil") +#3、code相等 + def assert_code(self,code,expected_code): + """ + 验证返回状态码 + :param code: + :param expected_code: + :return: + """ + try: + assert int(code) == int(expected_code) + return True + except: + self.log.error("code error,code is %s,expected_code is %s"%(code,expected_code)) + + raise +#4、body相等 + def assert_body(self,body,expected_body): + """ + 验证返回结果内容相等 + :param body: + :param expected_body: + :return: + """ + try : + assert body == expected_body + return True + except: + self.log.error("body error,body is %s,expected_body is %s"%(body,expected_body)) + raise +#5、body包含 + def assert_in_body(self,body,expected_body): + """ + 验证返回结果是否包含期望的结果 + :param body: + :param expected_body: + :return: + """ + try: + body = json.dumps(body) + print(body) + assert expected_body in body + return True + except: + self.log.error("不包含或者body是错误,body is %s,expected_body is %s"%(body,expected_body)) + raise +``` + +写好了Utils目录下的AssertUtil.py文件,接下来我们要开始在test_mall.py中调用该断言方法对返回状态码和结果进行验证。 + +```python +from utils.AssertUtil import AssertUtil + +# 测试返回的code1 +#返回状态码 +code = r["code"] +#assert code == 200 +AssertUtil().assert_code(code,200) + +#返回结果内容 +#body = json.dumps(r["body"]) +body = r["body"] +#assert '"user_id": 1, "username": "python"' in body +AssertUtil().assert_in_body(body,'"user_id": 1, "username": "python"') + +``` + +## 数据库结果断言 + +大多数情况下,我们接口自动化测试需要对数据库进行验证,比如说,我们在注册接口中,需要验证数据库中是否有新增的数据,这时候我们就需要对数据库进行查询,然后对查询结果进行断言。比如说,我们需要对订单接口进行测试,需要验证数据库中的订单数据是否正确,这时候我们就需要对数据库进行查询,然后对查询结果进行断言。 + +### PyMySQL安装以及简单使用 + +PyMySQL是Python的一个MySQL数据库驱动,它实现了Python DB API 2.0规范的全部标准,基于纯Python语言编写,所以它不依赖任何其他的库,可以很好的和Twisted,web.py等框架集成。 + +PyMySQL的安装非常简单,直接使用pip命令即可安装。 + +```python +pip install pymysql +``` + +或者在requirements.txt文件中添加pymysql依赖,然后使用pip install -r requirements.txt命令安装。 + +pymsql的使用非常简单,下面是一个简单的例子。 + +```python +import pymysql +# 链接数据库 +conn = pymysql.connect(host= "database address",user ="user name",password="password",database="database name",charset="utf8") +# 获取执行sql语句的光标对象 +cursor = conn.cursor() # 结果为元组 +# 获取执行SQL语句,结果作为字典返回 +cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) +# define the execute sql +sql = "select username,password from tb_user" +# execute sql +cursor.exectute(sql) +# fetchone()获取单条数据 +result = cursor.fetchone() +# fetchall()获取所有数据 +results = cursor.fetchall() +# 关闭光标对象 +cursor.close() +# 关闭数据库连接 +conn.close() +``` + +### 工具类封装及使用 + +在utils目录下新建一个MysqlUtil.py文件,用于封装数据库操作的方法。 + +```python +from utils.LogUtil import my_log +import pymysql +#1、创建封装类 +class Mysql: +#2、初始化数据,连接数据库,光标对象 + def __init__(self,host,user,password,database,charset="utf8",port=3306): + self.log = my_log() + self.conn = pymysql.connect( + host=host, + user=user, + password=password, + database=database, + charset=charset, + port=port + ) + self.cursor = self.conn.cursor(cursor=pymysql.cursors.DictCursor) +#3、创建查询、执行方法 + def fetchone(self,sql): + """ + 单个查询 + :param sql: + :return: + """ + self.cursor.execute(sql) + return self.cursor.fetchone() + + def fetchall(self,sql): + """ + 多个查询 + :param sql: + :return: + """ + self.cursor.execute(sql) + return self.cursor.fetchall() + + def exec(self,sql): + """ + 执行 + :return: + """ + try: + if self.conn and self.cursor: + self.cursor.execute(sql) + self.conn.commit() + except Exception as ex: + self.conn.rollback() + self.log.error("Mysql 执行失败") + self.log.error(ex) + return False + return True + +#4、关闭对象 + def __del__(self): + #关闭光标对象 + if self.cursor is not None: + self.cursor.close() + #关闭连接对象 + if self.conn is not None: + self.cursor.close() + +if __name__ == "__main__": + mysql = Mysql("211.103.136.242", + "test", + "test123456","meiduo", + charset="utf8", + port=7090) + #res = mysql.fetchall("select username,password from tb_users") + # 执行,修改数据库里面的数据 + res = mysql.exec("update tb_users set first_name='python' where username = 'python'") + print(res) + +``` + +### 数据库断言之配置文件 + +我们上一步把数据库操作封装成了一个工具类,但是在使用的时候,我们需要把数据库的连接信息写死在代码里面,这样的话,如果我们的数据库信息发生了变化,我们就需要修改代码,这样的话,我们的代码就不够灵活,所以我们需要把数据库的连接信息放到配置文件config里面,然后在代码里面读取配置文件,这样的话,我们的代码就可以灵活的修改数据库的连接信息了。 + +我们之前在config文件夹下面已经有了一个config.yaml文件;但是还是建议大家把数据库的连接信息放到一个单独的文件db_conf.yml里面,这样的话,我们的配置文件就更加清晰了。 + +步骤如下: +* 在config文件夹下面新建一个db_conf.yml文件; +* 在db_conf.yml文件里面写入数据库的连接信息; + +```yaml +#编写相关基本信息 +db_1: + db_host: "211.103.136.242" + db_user: "test" + db_password: "test123456" + db_name: "meiduo" + db_charset: "utf8" + db_port: "7090" + +db_2: + db_host: "111211.103.136.242" + db_user: "test" + db_password: "test123456" + db_name: "meiduo" + db_charset: "utf8" + db_port: "7090" + +db_3: + db_host: "333211.103.136.242" + db_user: "test" + db_password: "test123456" + db_name: "meiduo" + db_charset: "utf8" + db_port: "7090" +``` +* 重构config.py,需要添加如下代码: + +```python +#定义db_conf.yml路径 +_db_config_file = _config_path + os.sep +"db_conf.yml" + +def get_db_config_file(): + return _db_config_file + +class ConfigYaml: + def get_db_conf_info(self,db_alias): + """ + 根据db_alias获取该名称下的数据库信息 + :param db_alias: + :return: + """ + return self.db_config[db_alias] +if __name__ == "__main__": + conf_read = ConfigYaml() + print(conf_read.get_db_conf_info("db_1")) + print(conf_read.get_db_conf_info("db_2")) + print(conf_read.get_db_conf_info("db_3")) + +``` +### 数据库断言之结果验证 +* 执行,结果验证 + + 1、初始化数据库信息,在common文件夹下面创建Base.py,用来init_db + ```python + from config.Conf import ConfigYaml + from utils.MysqlUtil import Mysql + + #1、定义init_db + def init_db(db_alias): + + #2、初始数据化信息,通过配置 + db_info = ConfigYaml().get_db_conf_info(db_alias) + host = db_info["db_host"] + user = db_info["db_user"] + password = db_info["db_password"] + db_name = db_info["db_name"] + charset = db_info["db_charset"] + port = int(db_info["db_port"]) + + #3、初始化mysql对象 + conn = Mysql(host,user,password,db_name,charset,port) + print(conn) + return conn + + if __name__ =="__main__": + init_db("db_1") + + ``` + + 2、接口用例返回结果内容进数据库验证,在test_mall.py文件中添加如下代码: + + ```python + #1、导入init_db + from common.Base import init_db + + #1、初始化数据库对象 + conn = init_db("db_1") + #2、查询结果 + res_db = conn.fetchone("select id,username from tb_users where username='python'") + print("数据库查询结果",res_db) + #3、验证 + user_id = body["user_id"] + assert user_id == res_db["id"] + + ``` \ No newline at end of file diff --git "a/_posts/2023-01-28-\346\227\245\345\277\227\346\226\207\344\273\266.md" "b/_posts/2023-01-28-\346\227\245\345\277\227\346\226\207\344\273\266.md" new file mode 100644 index 0000000000..aff7f41af3 --- /dev/null +++ "b/_posts/2023-01-28-\346\227\245\345\277\227\346\226\207\344\273\266.md" @@ -0,0 +1,234 @@ +--- +layout: post +title: "日志文件" +date: 2023-01-28 +description: "主要包括日志文件介绍以及快速使用,文件封装,日志配置文件重构,日志工具类应用等等内容。" +tag: 自动化测试 +--- + +主要包括日志文件介绍以及快速使用,文件封装,日志配置文件重构,日志工具类应用等等内容。 + +# 日志文件 +## 简介 +logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级/日志保存路径等 +## 基本使用 +在testcase文件夹下面,新建t_log文件夹,创建log_demo.py文件,这个文件夹用来存储日志有关的测试用例。 +```python +# 1. 导入logging +import logging +# 2. set the basic information +logging.basicConfig(level = logging.INFO,format='%(asctime)s-%(levelname)s-%(message)s') +# format: 时间-日志等级-日志信息 %括号里面的内容可以自定义 +# level: INFO表示,只有INFO级别的日志才会被输出,如INFO,WARNING,ERROR,CRITICAL +# 如果是DEBUG级别的日志,就不会被输出 +#3、定义日志名称getlogger +logger = logging.getLogger("log_demo") +#4、info,debug +logger.info("info") # 只有INFO级别的日志才会被输出 +logger.debug("debug") # 不会被输出 +logger.warning("warning") # WARNING级别的日志会被输出 +``` + +``` +执行这段代码,结果如下: +```console +2023-01-17 09:28:54,466-INFO-info +2023-01-17 09:28:54,466-WARNING-warning +``` + +### 日志输出到控制台,文件,指定文件 +1. 设置logger名称 +2. 设置logger级别 +3. 创建handler,用于输出控制台或者写入日志文件 +4. 设置日志级别 +5. 定义handler的输出格式 +6. 给logger添加handler + +在testcase文件夹下面的t_log文件夹下面,创建log_file_demo.py文件,输出到控制台. + +```python +# 输出到控制台 +import logging +# 1. 设置logger名称 +logger = logging.getLogger("log_file_demo") +# 2. 设置logger级别,这里设置为INFO,只有INFO级别的日志才会被输出 +logger.setLevel(logging.INFO) +# 3. 创建handler,用于输出控制台或者写入日志文件 +fh_stream = logging.StreamHandler() # StreamHandler表示输出到控制台 +# 4. 设置日志级别 +fh_stream.setLevel(logging.INFO) +# 5. 定义handler的输出格式,这里设置为时间-日志等级-日志信息 +formatter = logging.Formatter('%(asctime)s %(name)s %(levelname)s') +fh_stream.setFormatter(formatter) +# 6. 给logger添加handler,这里添加的是fh_stream,也就是输出到控制台,如果想输出到文件,可以添加fh_file +logger.addHandler(fh_stream) +#7、运行输出 +logger.info("this is a info") +logger.debug("this is a debug") +logger.warning("this is a warning") +``` + +在控制台查看输出结果: +```console +2023-01-17 10:04:31,711 log_file_demo INFO +2023-01-17 10:04:31,711 log_file_demo WARNING +``` + +输出到文件: +```python +#输出到文件 +import logging +#1、设置logger名称 +logger = logging.getLogger("log_file_demo") +#2、设置log级别 +logger.setLevel(logging.INFO) +#3、创建handler +#写入文件 +fh_file = logging.FileHandler("./test.log") +#4、设置日志级别 +fh_file.setLevel(logging.WARNING) +#5、定义输出格式 +formatter = logging.Formatter('%(asctime)s %(name)s %(levelname)s %(message)s ') +fh_file.setFormatter(formatter) +#6、添加handler +logger.addHandler(fh_file) +#7、运行输出 +logger.info("this is a info") +logger.debug("this is a debug") +logger.warning("this is a warning") +``` + +结果会输出到同文件目录下的test.log文件中,内容如下: +```console +2023-01-17 10:07:10,826 log_file_demo WARNING this is a warning +``` +为啥这里只有warning级别的日志被输出到文件中呢? +虽然日志级别设置为INFO,但是在添加handler的时候,设置了日志级别为WARNING,所以只有WARNING级别的日志会被输出到文件中。 + +#### Format格式说明 + +|格式|说明| +|:---|:---| +|%(name)s|Logger的名字| + | %(levelno)s| 数字形式的日志级别| +| %(levelname)s| 文本形式的日志级别| +| %(pathname)s| 调用日志输出函数的模块的完整路径名,可能没有| +| %(filename)s| 调用日志输出函数的模块的文件名| +| %(asctime)s| 字符串形式的当前时间。默认格式是“2003-07-08 16:49:45,896”。逗号后面的是毫秒| +| %(message)s|用户输出的消息| + +## 日志文件的封装 + +1. 封装Log工具类 +在utils文件夹下面创建LogUtil.py文件,封装Log工具类,代码如下: + +```python +# 创建类 +import logging +# 定义日志级别关系映射 +# 定义之后,就可以用log_l[self.log_level]去更新原来的self.log_level +log_l = { + "debug":logging.DEBUG, + "info":logging.INFO, + "warning":logging.WARNING, + "error":logging.ERROR +} +class Logger: +# 定义参数 +## 输出文件名称,Loggername,日志级别 + def __init__(self,log_file,log_name,log_level): + self.log_file = log_file + self.log_name = log_name + self.log_level = log_level + # 编写输出控制台或者文件,利用之前写好的log_file_demo.py + #1、设置logger名称 + self.logger = logging.getLogger(self.log_name) + #2、设置log级别 + self.logger.setLevel(log_l[self.log_level]) + # 首先判断handler是否存在 创建handler + if not self.logger.handlers: + + formatter = logging.Formatter('%(asctime)s %(name)s %(levelname)s %(message)s ') + # 输出到控制台 + fh_stream = logging.StreamHandler() + fh_stream.setLevel(log_l[self.log_level]) + fh_stream.setFormatter(formatter) + + #写入文件 + fh_file = logging.FileHandler(self.log_file) + fh_file.setLevel(log_l[self.log_level]) + fh_file.setFormatter(formatter) + #6、添加handler + self.logger.addHandler(fh_file) + self.logger.addHandler(fh_stream) + +``` +2. 重构配置文件 + +根据我们封装的LogUtil.py文件,重构配置文件,需要的配置信息包括: log_file,log_lovel + +log_name作为参数传入,不写在配置文件中。 + +打开config文件夹下面的conf.yml文件,修改配置信息,添加如下信息代码如下: +```yaml +BASE: + # log + log_level: "debug" + log_extension: ".log" +``` + +然后修改config.py文件,添加如下代码(第一步,定义logs文件路径;第二步,获取该Log文件路径;第三步,根据该路经获取日志级别和文件扩展名: +```python +# 定义logs文件路径 +_log_path = base_dir + os.sep + "logs" #下划线开头的变量,表示私有变量 +# 获取该Log文件路径 +def get_log_path(): + """ + 获取log文件路径 + """ + return _log_path +# 根据该路经获取日志级别和文件扩展名 +def get_conf_log(self): + return self.config["BASE"]["log_level"] +# 获取文件扩展名 +def get_config_log_extension(self): + return self.config["BASE"]["log_extension"] +``` + +测试以下,在config.py文件的if __name__ == '__main__':中执行代码: +```python +if __name__ == '__main__': + conf_read = ConfigYaml() + # print(conf_read.get_conf_url()) + print(conf_read.get_conf_log()) + print(conf_read.get_config_log_extension()) +``` +3. 日志工具应用 + +在LogUtil.py文件中,首先初始化参数数据,然后对外提供方法,初始化log工具类,提供其他类使用。 + +```python +from config import config +from config.config import ConfigYaml +import datetime +import os + +# 初始化参数数据 +# 日志文件路径 +log_path = config.get_log_path() +# 获取当前时间 +current_time = datetime.datetime.now().strftime("%Y-%m-%d") +# 获取扩展名 +log_extension = ConfigYaml.get_config_log_extension() +logfile = os.path.join(log_path+current_time+log_extension) +# 日志文件级别 +loglevel = ConfigYaml.get_conf_log() + +# 2. 对外方法,初始化log工具类,提供其他类使用 +def my_log(): + return Logger(log_file=logfile,log_name=log_name,log_level=loglevel).logger + +if __name__ == "__main__": + my_log().debug("this is a bug") + +``` \ No newline at end of file diff --git "a/_posts/2023-01-29-\346\225\260\346\215\256\351\251\261\345\212\250\347\232\204\350\207\252\345\212\250\345\214\226\346\265\213\350\257\2251.md" "b/_posts/2023-01-29-\346\225\260\346\215\256\351\251\261\345\212\250\347\232\204\350\207\252\345\212\250\345\214\226\346\265\213\350\257\2251.md" new file mode 100644 index 0000000000..1ba20eacae --- /dev/null +++ "b/_posts/2023-01-29-\346\225\260\346\215\256\351\251\261\345\212\250\347\232\204\350\207\252\345\212\250\345\214\226\346\265\213\350\257\2251.md" @@ -0,0 +1,343 @@ +--- +layout: post +title: "数据驱动的自动化测试1" +date: 2023-01-29 +description: "包括yaml和Excel用例测试和数据驱动开发。" +tag: 自动化测试 +--- + +包括yaml和Excel用例测试和数据驱动开发。 + +# 数据驱动的自动化测试 + +## 什么是数据驱动 + +数据驱动是指将测试数据与测试代码分离,将测试数据存储在一个独立的文件中,然后在测试代码中读取测试数据,最后将测试数据作为参数传递给测试代码。这样做的好处是,测试数据和测试代码分离,测试数据可以独立于测试代码进行修改,测试代码可以独立于测试数据进行修改,测试数据可以被多个测试用例共享,测试数据可以被多个测试框架共享。 + +## yaml数据驱动 + +yaml是一种数据序列化格式,可以用来存储数据,也可以用来存储配置信息。yaml的语法比较简单,可以用来存储简单的数据结构,比如列表、字典、字符串、数字等。 + +首先,在data文件夹下面创建所需要用到的测试数据文件,比如testlogin.yaml,然后在login_data.yaml文件中存储测试数据,比如: + +```yaml + +#登录测试用例 +# ---用来分割不同的测试用例 +--- +#测试用例名称 +"case_name": "登录成功用例" +#url地址 +"url": "/authorizations/" +#data +"data": + username: "python" + password: "12345678" +#期望结果 +"expect": '"user_id": 1, "username": "python"' +--- +#测试用例名称 +"case_name": "登录失败用例" +#url地址 +"url": "/authorizations/" +#data +"data": + username: "test123456" + password: "111111" +#期望结果 +"expect": '"user_id": 1, "username": "python"' +``` + +在这个测试数据着哦功能,当输入用户名和密码正确时,登录成功,当输入用户名和密码错误时,登录失败。 + +接下来,在测试代码中读取测试数据,执行测试用例,然后将测试数据作为参数传递给测试代码(也即是参数化),比如:在testcases文件夹下面创建test_login.py文件,然后在test_login.py文件中编写测试代码. + +这个测试代码应该包括以下内容: +1. 获取测试用例内容,包括获取testlogin.yaml文件的文件路径,使用工具类来读取多个文档内容。 + 1. 获取testlogin.yaml文件的文件路径:在config文件夹下面的Conf.py文件中添加获取testlogin.yaml文件的文件路径的方法,比如: + ```python + import os + from utils.YamlUtil import YamlReader + #1、获取项目基本目录 + #获取当前项目的绝对路径 + current = os.path.abspath(__file__) + #print(current) + BASE_DIR = os.path.dirname(os.path.dirname(current)) + #定义data目录的路径 + _data_path = BASE_DIR + os.sep + "data" # _开头的变量表示私有变量 + def get_data_path(): + return _data_path + ``` + +2. 参数化执行测试用例,使用pytest.mark.parametrize来实现参数化。 + +test_login.py文件代码如下: + +```python +from config import Conf +import os +from utils.YamlUtil import YamlReader +import pytest +from config.Conf import ConfigYaml +from utils.RequestsUtil import Request +#1、获取测试用例内容list +#获取testlogin.yml文件路径 +test_file = os.path.join(Conf.get_data_path(),"testlogin.yml") +#print(test_file) +#使用工具类来读取多个文档内容 +data_list = YamlReader(test_file).data_all() +print(data_list) +#2、参数化执行测试用例 + +@pytest.mark.parametrize("login",data_list) +def test_yaml(login): + #初始化url,data + url = ConfigYaml().get_conf_url()+login["url"] + print("url %s"%url) + data = login["data"] + print("data %s"%data) + #post请求 + request = Request() + res = request.post(url,json=data) + #打印结果 + print(res) + +if __name__ == "__main__": + pytest.main(["-s","Test_login.py"]) + +``` + +到这里,我们已经完成了测试数据的编写,以及测试代码文档的编写,然后我们编写了使用登录的测试例,通过测试用例参数化去获取这个列表,最后我们还需要对得到的结果进行断言,断言的结果是否和我们期望的结果一致,如果一致,那么就是测试通过,如果不一致,那么就是测试失败。 + +## Excel数据驱动开发 + +在上面的测试用例中,我们使用了yaml文件来存储测试数据,但是yaml文件的可读性不是很好,所以我们可以使用Excel来存储测试数据,然后使用python来读取Excel中的数据,然后再进行参数化。 + +这一节内容主要包括Excel用例设计,Excel读取以及Excel参数化运行等等。 + +### Excel用例设计 + +EXCEL用例设计非常重要,要充分全面的覆盖到测试用例的各个方面,比如:测试用例编号、测试用例名称、测试用例描述、测试用例前置条件、测试用例步骤、测试用例预期结果、测试用例实际结果、测试用例执行结果等等。 + + +![Excel用例设计](/images/posts/2023-01-29-数据驱动的自动化测试1/Excel用例设计.png) + +### Excel读取 + +在testcase文件夹下面创建文件夹t_excel,然后在t_excel文件夹下面创建文件excel_demo.py,然后在excel_demo.py文件中编写代码,代码如下: + +在这里要注意xlrd读取excel文件的具体报错。 + +xlrd==1.2.0,报错信息如下:(在新版python3.10中,更新删除了getiterator方法,所以我们老版本的xlrd库调用getiterator方法时会报错。) +```console +AttributeError: 'ElementTree' object has no attribute 'getiterator' +``` +解决方法: +通过pip show xlrd命令,找出目录python\Lib\site-packages\xlrd下的xlsx.py文件 + +修改两个地方的的getiterator()改成iter(),下面的两个地方,这里已经把getiterator()改成iter()了 + +```python +#1、导入包,xlrd +import xlrd +#2、创建workbook对象,这里先把excel文件拷贝一份到当前目录下 +book = xlrd.open_workbook("testdata.xlsx") +#3、sheet对象 +#索引 +#sheet = book.sheet_by_index(0) +#名称 +sheet = book.sheet_by_name("美多商城接口测试") +#4、获取行数和列数 +rows = sheet.nrows #行数 +cols = sheet.ncols #列数 +#5、读取每行的内容 +for r in range(rows): + r_values = sheet.row_values(r) + #print(r_values) +#6、读取每列的内容 +for c in range(cols): + c_values = sheet.col_values(c) + #print(c_values) +#7、读取固定列的内容 +print(sheet.cell(1,1)) + +``` + +### Excel参数化运行 + +在utils文件夹下面创建ExcelUtil.py文件,然后在ExcelUtil.py文件中编写代码,主要用于参数化,代码如下: + +```python +import os +import xlrd + +#目的:参数化,pytest 需要返回的数据用list来封装 +#自定义异常 +class SheetTypeError: + pass +#1、验证文件是否存在,存在读取,不存在报错 +class ExcelReader: + def __init__(self,excel_file,sheet_by): + if os.path.exists(excel_file): + self.excel_file = excel_file + self.sheet_by = sheet_by + self._data=list() + else: + raise FileNotFoundError("文件不存在") +#2、读取sheet方式,名称,索引 + def data(self): + #存在不读取,不存在读取 + if not self._data: + workbook = xlrd.open_workbook(self.excel_file) + if type(self.sheet_by) not in [str,int]: + raise SheetTypeError("请输入Int or Str") + elif type(self.sheet_by) == int: + sheet = workbook.sheet_by_index(self.sheet_by) + elif type(self.sheet_by) == str: + sheet = workbook.sheet_by_name(self.sheet_by) + #3、读取sheet内容 + #返回list,元素:字典 + #格式[{"a":"a1","b":"b1"},{"a":"a2","b":"b2"}] + #1.获取首行的信息 + title = sheet.row_values(0) + #2.遍历测试行,与首行组成dict,放在list + #1 循环,过滤首行,从1开始 + for col in range(1,sheet.nrows): + col_value = sheet.row_values(col) + #2 与首组成字典,放list + self._data.append(dict(zip(title, col_value))) + +#4、结果返回 + return self._data + +# head = ["a","b"] +# value1 = ["a1","b1"] +# value2 = ["a2","b2"] +# data_list= list() +# #zip +# data_list.append(dict(zip(head,value1))) +# data_list.append(dict(zip(head,value2))) +# #print(dict(zip(head,value1))) +# #print(dict(zip(head,value2))) +# print(data_list) + +if __name__ == "__main__": + reader = ExcelReader("../data/testdata.xlsx","美多商城接口测试") + print(reader.data()) +``` + +然后,获取是否运行,参数化,并进行结果断言。 + +#### 获取是否运行 + +获取是否运行:在common文件夹下面创建ExcelData.py文件,然后在ExcelData.py文件中编写代码,代码如下: + +```python +from utils.ExcelUtil import ExcelReader +from common.ExcelConfig import DataConfig + +class Data: + def __init__(self,testcase_file,sheet_name): + #1、使用excel工具类,获取结果list + #self.reader = ExcelReader("../data/testdata.xlsx","美多商城接口测试") + self.reader = ExcelReader(testcase_file,sheet_name) + #print(reader.data()) + #2、列是否运行内容,y + def get_run_data(self): + """ + 根据是否运行列==y,获取执行测试用例 + :return: + """ + run_list = list() + for line in self.reader.data(): + if str(line[DataConfig().is_run]).lower() == "y": + #print(line) + #3、保存要执行结果,放到新的列表。 + run_list.append(line) + print(run_list) + return run_list + + def get_case_list(self): + """ + 获取全部测试用例 + :return: + """ + # run_list=list() + # for line in self.reader.data(): + # run_list.append(line) + run_list = [ line for line in self.reader.data()] + return run_list + + def get_case_pre(self,pre): + #获取全部测试用例 + #list判断,执行,获取 + """ + 根据前置条件:从全部测试用例取到测试用例 + :param pre: + :return: + """ + run_list = self.get_case_list() + for line in run_list: + if pre in dict(line).values(): + return line + return None +``` + +其中,我们这个是根据是否运行列==y,获取执行测试用例,还需要创建一个ExcelConfig.py文件,来表示对应的映射关系。代码如下: + +```python + + +#定义类 +class DataConfig: +#定义列属性 + #用例ID 模块 接口名称 请求URL 前置条件 请求类型 请求参数类型 + #请求参数 预期结果 实际结果 备注 是否运行 headers cookies status_code 数据库验证 + #用例ID + case_id = "用例ID" + case_model = "模块" + case_name = "接口名称" + url = "请求URL" + pre_exec = "前置条件" + method = "请求类型" + params_type = "请求参数类型" + params = "请求参数" + expect_result = "预期结果" + actual_result = "实际结果" + is_run = "是否运行" + headers = "headers" + cookies = "cookies" + code = "status_code" + db_verify = "数据库验证" + +``` + +我们还需要获取testcase_file,sheet_name,所以我们需要修改config文件夹下面的config.yml,代码如下: + +```yaml +BASE: + test: + url: "http://211.103.136.242:8064" + case_file: "testdata.xlsx" + case_sheet: "美多商城接口测试" +``` + +改完之后,需要修改Conf.py文件,获取测试用例sheet名称,代码如下: + +```python +class ConfigYaml: + #定义方法获取需要信息 + def get_excel_file(self): + """ + 获取测试用例excel名称 + :return: + """ + return self.config["BASE"]["test"]["case_file"] + + def get_excel_sheet(self): + """ + 获取测试用例sheet名称 + :return: + """ + return self.config["BASE"]["test"]["case_sheet"] +``` \ No newline at end of file diff --git "a/_posts/2023-01-29-\346\225\260\346\215\256\351\251\261\345\212\250\347\232\204\350\207\252\345\212\250\345\214\226\346\265\213\350\257\2252.md" "b/_posts/2023-01-29-\346\225\260\346\215\256\351\251\261\345\212\250\347\232\204\350\207\252\345\212\250\345\214\226\346\265\213\350\257\2252.md" new file mode 100644 index 0000000000..ecef2df209 --- /dev/null +++ "b/_posts/2023-01-29-\346\225\260\346\215\256\351\251\261\345\212\250\347\232\204\350\207\252\345\212\250\345\214\226\346\265\213\350\257\2252.md" @@ -0,0 +1,267 @@ +--- +layout: post +title: "数据驱动的自动化测试2" +date: 2023-01-29 +description: "Excel参数化之参数化运行" +tag: 自动化测试 +--- + +### Excel参数化之参数化运行 + +在testcase文件夹下面创建test_excel_case.py文件,代码如下: + +```python +from config.Conf import ConfigYaml +from config import Conf +import os +from common.ExcelData import Data +from utils.LogUtil import my_log +from common import ExcelConfig +from utils.RequestsUtil import Request +import json +import pytest +from common import Base +from utils.AssertUtil import AssertUtil +import allure +#1、初始化信息 +#1).初始化测试用例文件 +case_file = os.path.join(Conf.get_data_path(),ConfigYaml().get_excel_file()) +#2).测试用例sheet名称 +sheet_name = ConfigYaml().get_excel_sheet() +#3).获取运行测试用例列表 +data_init = Data(case_file,sheet_name) +run_list = data_init.get_run_data() + +#4).日志 +log = my_log() +#初始化dataconfig +data_key = ExcelConfig.DataConfig +#2、测试用例方法,参数化运行 +#一个用例的执行 +class TestExcel: + #1、增加Pyest + #2、修改方法参数 + #3、重构函数内容 + #4、pytest.main + + def run_api(self,url,method,params=None,header=None,cookie=None): + """ + 发送请求api + :return: + """ + # 2).接口请求 + request = Request() + # params 转义json + # 验证params有没有内容 + if len(str(params).strip()) is not 0: + params = json.loads(params) + # method post/get + if str(method).lower() == "get": + # 2.增加Headers + res = request.get(url, json=params, headers=header, cookies=cookie) + elif str(method).lower() == "post": + res = request.post(url, json=params, headers=header, cookies=cookie) + else: + log.error("错误请求method: %s" % method) + + return res + + def run_pre(self,pre_case): + #初始化数据 + pass + url = ConfigYaml().get_conf_url()+pre_case[data_key.url] + method = pre_case[data_key.method] + params = pre_case[data_key.params] + headers = pre_case[data_key.headers] + cookies = pre_case[data_key.cookies] + + # 1.判断headers是否存在,json转义,无需 + # if headers: + # header = json.loads(headers) + # else: + # header = headers + header = Base.json_parse(headers) + # 3.增加cookies + # if cookies: + # cookie = json.loads(cookies) + # else: + # cookie = cookies + cookie = Base.json_parse(cookies) + res = self.run_api(url,method,params,header) + print("前置用例执行:%s"%res) + return res + +#1).初始化信息,url,data + + # 1、增加Pyest + @pytest.mark.parametrize("case",run_list) + # 2、修改方法参数 + def test_run(self,case): + # 3、重构函数内容 + #data_key = ExcelConfig.DataConfig + # run_list第1个用例,用例,key获取values + url = ConfigYaml().get_conf_url()+case[data_key.url] + print(url) + case_id = case[data_key.case_id] + case_model = case[data_key.case_model] + case_name = case[data_key.case_name] + pre_exec = case[data_key.pre_exec] + method = case[data_key.method] + params_type = case[data_key.params_type] + params = case[data_key.params] + expect_result = case[data_key.expect_result] + headers = case[data_key.headers] + cookies =case[data_key.cookies] + code = case[data_key.code] + db_verify = case[data_key.db_verify] + + + + # 1、验证前置条件 + if pre_exec: + pass + # 2、找到执行用例 + # 前置测试用例 + pre_case = data_init.get_case_pre(pre_exec) + print("前置条件信息为:%s"%pre_case) + pre_res = self.run_pre(pre_case) + headers,cookies = self.get_correlation(headers,cookies,pre_res) + + header = Base.json_parse(headers) + cookie = Base.json_parse(cookies) + res = self.run_api(url, method, params, header,cookie) + print("测试用例执行:%s" % res) + + #allure + #sheet名称 feature 一级标签 + allure.dynamic.feature(sheet_name) + #模块 story 二级标签 + allure.dynamic.story(case_model) + #用例ID+接口名称 title + allure.dynamic.title(case_id+case_name) + #请求URL 请求类型 期望结果 实际结果描述 + desc = "请求URL: {}
" \ + "请求类型: {}
" \ + "期望结果: {}
" \ + "实际结果: {}".format(url,method,expect_result,res) + allure.dynamic.description(desc) + + #断言验证 + #状态码,返回结果内容,数据库相关的结果的验证 + #状态码 + assert_util = AssertUtil() + assert_util.assert_code(int(res["code"]),int(code)) + #返回结果内容 + assert_util.assert_in_body(str(res["body"]),str(expect_result)) + #数据库结果断言 + Base.assert_db("db_1",res["body"],db_verify) + + + #1、初始化数据库 + # from common.Base import init_db + # sql = init_db("db_1") + # #2、查询sql,excel定义好的 + # db_res = sql.fetchone(db_verify) + # log.debug("数据库查询结果:{}".format(str(db_res))) + # #3、数据库的结果与接口返回的结果验证 + # #获取数据库结果的key + # verify_list = list(dict(db_res).keys()) + # #根据key获取数据库结果,接口结果 + # for line in verify_list: + # res_line = res["body"][line] + # res_db_line = dict(db_res)[line] + # #验证 + # assert_util.assert_body(res_line,res_db_line) + # 1.判断headers是否存在,json转义,无需 + # if headers: + # header = json.loads(headers) + # else: + # header = headers + # # 3.增加cookies + # if cookies: + # cookie = json.loads(cookies) + # else: + # cookie = cookies + + # #2).接口请求 + # request = Request() + # #params 转义json + # #验证params有没有内容 + # if len(str(params).strip()) is not 0: + # params = json.loads(params) + # #method post/get + # if str(method).lower()=="get": + # # 2.增加Headers + # res = request.get(url,json=params,headers = header,cookies=cookie) + # elif str(method).lower()=="post": + # res = request.post(url, json=params,headers = header,cookies=cookie) + # else: + # log.error("错误请求method: %s"%method) + # print(res) +#TestExcel().test_run() + + def get_correlation(self,headers,cookies,pre_res): + """ + 关联 + :param headers: + :param cookies: + :param pre_res: + :return: + """ + #验证是否有关联 + headers_para,cookies_para = Base.params_find(headers,cookies) + #有关联,执行前置用例,获取结果 + if len(headers_para): + headers_data = pre_res["body"][headers_para[0]] + #结果替换 + headers = Base.res_sub(headers,headers_data) + if len(cookies_para): + cookies_data = pre_res["body"][cookies_para[0]] + # 结果替换 + cookies = Base.res_sub(headers, cookies_data) + return headers,cookies +if __name__ == '__main__': + #pass + report_path = Conf.get_report_path()+os.sep+"result" + report_html_path = Conf.get_report_path()+os.sep+"html" + pytest.main(["-s","test_excel_case.py","--alluredir",report_path]) + + + #Base.allure_report("./report/result","./report/html") + #Base.send_mail(title="接口测试报告结果",content=report_html_path) + #固定headers请求 + #1.判断headers是否存在,json转义,无需 + #2.增加Headers + #3.增加cookies + #4.发送请求 + + #动态关联 + #1、验证前置条件 + #if pre_exec: + #pass + #2、找到执行用例 + #3、发送请求,获取前置用例结果 + #发送获取前置测试用例,用例结果 + #数据初始化,get/post,重构 + #4、替换Headers变量 + #1、验证请求中是否${}$,返回${}$内容 + # str1 = '{"Authorization": "JWT ${token}$"}' + # if "${" in str1: + # print(str1) + # import re + # pattern = re.compile('\${(.*)}\$') + # re_res = pattern.findall(str1) + # print(re_res[0]) + # #2、根据内容token,查询 前置条件测试用例返回结果token = 值 + # token = "123" + # #3、根据变量结果内容,替换 + # res = re.sub(pattern,token,str1) + # print(res) + #5、请求发送 + + #1、查询,公共方法 + #2、替换,公共方法 + #3、验证请求中是否${}$,返回${}$内容,公共方法 + #4、关联方法 +``` + diff --git "a/_posts/2023-01-30-Allure\346\212\245\345\221\212.md" "b/_posts/2023-01-30-Allure\346\212\245\345\221\212.md" new file mode 100644 index 0000000000..cb618b4116 --- /dev/null +++ "b/_posts/2023-01-30-Allure\346\212\245\345\221\212.md" @@ -0,0 +1,266 @@ +--- +layout: post +title: "Allure报告" +date: 2023-01-30 +description: "介绍了Allure的安装,环境配置;Allure命令,配置pytest.ini文件,利用allure工具生成html报告;最后,讲了Allure的应用实例。" +tag: 自动化测试 +--- + +介绍了Allure的安装,环境配置;Allure命令,配置pytest.ini文件,利用allure工具生成html报告;最后,讲了Allure的应用实例。 + +# Allure + +## 快速入门 + +### 安装 + +1. 安装allure的python插件 + +```bash +pip install allure-pytest -i https://mirrors.aliyun.com/pypi/simple/ +``` +或者写入requirements.txt + +```bash +allure-pytest==2.8.18 +``` + +```bash +pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ +``` + +2. allure工具的安装 +allure的python插件(而且生成的不是html格式的文件)依赖allure工具,所以需要先安装allure工具: + +allure需要有jdk环境,所以需要先安装jdk。 + +```bash +sudo apt-get update +sudo apt-get install openjdk-8-jdk +sudo update-alternatives --set java /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/java # 设置环境变量 +java -version # 显示版本信息表示安装成功 +``` + +安装Allure + +方法一: + +```bash +sudo apt-get install allure +# 查看安装路径 +whereis allure +# allure: /usr/share/allure +# 配置软连接或者环境变量 +sudo ln -s /usr/share/allure /usr/bin/allure +``` + + + +上述安装方式使用allure -- version时候报:allure: command can not found ,需要设置allure的bin放到PATH里边。 若whereis allure找不到allure,换下边方式安装。 + +方法二: + +```bash +curl -O https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/2.13.8/allure-commandline-2.13.8.tgz # 下载allure,文件会下载到当前目录;curl命令是linux下的下载工具,windows下可以用wget +sudo tar -zxvf allure-commandline-2.13.8.tgz -C /opt/ # -C指定解压目录,这里解压到/opt/allure-2.13.8;一般来说,/opt目录下的文件都是安装软件的. +sudo ln -s /opt/allure-2.13.8/bin/allure /usr/bin/allure +allure --version # 查看版本信息以及是否安装成功 +``` + +配置环境(软连接好像不起作用): + +```bash +vi /etc/profile +# 在PATH后追加以下内容,保存 +export PATH=$PATH:/home/allure-2.13.8/bin +# 使环境变量生效 +source /etc/profile +``` +最后,配置好环境变量后记得重启下ide(很重要)!!! +其他下载地址: +https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/ + +https://github.com/allure-framework/allure2/releases + + + +### 使用 + +首先要配置pytest.ini文件,告诉pytest使用allure插件;添加allure代码;运行;最后,利用allure工具生成html报告。 + +```ini +addopts = -s --alluredir ./report/result + +``` + +添加allure代码,在testcase文件夹下面创建t_allure文件夹,新建test_allure.py文件,添加如下代码: + +```python +import pytest +def test_1(): + print("test 1") + +def test_2(): + print("test 2") + +def test_3(): + print("test 3") + +if __name__ == '__main__': + pytest.main(["allure_demo.py"]) +``` + +运行代码,可以看到在同级report文件夹下生成了result文件夹,里边有allure相关的文件。 + +最后,点击terminal,在终端中输入allure generate ./report/result -o ./report/html --clean,生成html报告。 + +```bash +# 进入文件夹 +cd testcase/t_allure/ +ls +# 生成html报告 +allure generate ./report/result -o ./report/html --clean +# --clean表示清除之前的报告 -o表示输出的文件夹 +# 打开html报告,可以找到index.html文件,也可以使用allure open命令打开 +allure open ./report/html +``` + + +## Allure 命令 + +### Title + +可以自定义用例标题,标题默认为函数名。@allure.title("用例标题") + +### Description + +可以添加测试的详细说明,支持Markdown语法。@allure.description("用例详细描述") + +### Feature + +功能,可以理解为模块。@allure.feature("功能模块") + +### Story + +Story,可以理解为子模块。@allure.story("子模块") + +### Severity + +定义用例的严重程度,分为blocker、critical、normal、minor、trivial。@allure.severity(allure.severity_level.CRITICAL) + +默认为normal。 + +只运行指定级别的用例: + +```bash +pytest -s -q --alluredir=./report/result --allure-severities=critical # -s表示打印日志 -q表示简化输出;--allure-severities=critical表示只运行严重级别为critical的用例; +``` + + +### Allure.dynamic + +可以动态添加测试步骤,支持Markdown语法,可以使用函数体内的数据动态生成. + +@allure.dynamic.description("测试步骤") + +```python +@pytest.mark.parametrize("case",["case1","case2"]) +def test_4(self,case): + print(case) + allure.dynamic.title(case) +``` + +## Allure应用 + +### Allure测试用例 + +打开data文件集下面的testcase.xlsx文件,查看测试数据的具体信息,根据这个信息,对allure报告的内容进行如下设置: +```console +sheet名称 feature 一级标签 +模块 story 二级标签 +用例ID+接口名称 title +请求URL 请求类型 期望结果 实际结果描述 +``` + +```python +import allure +#allure +#sheet名称 feature 一级标签 +allure.dynamic.feature(sheet_name) +#模块 story 二级标签 +allure.dynamic.story(case_model) +#用例ID+接口名称 title +allure.dynamic.title(case_id+case_name) +#请求URL 请求类型 期望结果 实际结果描述 +desc = "请求URL: {}
" \ + "请求类型: {}
" \ + "期望结果: {}
" \ + "实际结果: {}".format(url,method,expect_result,res) +allure.dynamic.description(desc) +``` + +设置完成之后,每次都需要allure generate ./report/result -o ./report/html --clean来手动生成html报告,可以使用Subprocess来实现自动化生成报告。 + +Subprocess是Python中的一个模块,用于创建新进程,连接到它们的输入/输出/错误管道,并获取它们的返回代码。 + +方法: +* Subprocess.call():父进程等待子进程,返回退出信息(returncode,相当于linux exit code);0表示正常退出,非0表示异常退出。 +* shell=True:表示使用shell执行命令,否则默认使用cmd执行命令;支持命令以字符串的形式传入,不需要再使用列表的形式传入。 + +```python +import subprocess + + +res = subprocess.call(["ls","-l"]) +print(res) # 0代表正常退出,非0代表异常退出 + +subprocess.call("ls -l",shell=True) # shell=True,支持命令以字符串的形式传入 +``` + +输出结果: + +```console +总用量 4 +-rw-rw-r-- 1 hugo hugo 0 1月 30 10:00 __init__.py +-rw-rw-r-- 1 hugo hugo 138 1月 30 10:06 subprocess_demo.py +0 +总用量 4 +-rw-rw-r-- 1 hugo hugo 0 1月 30 10:00 __init__.py +-rw-rw-r-- 1 hugo hugo 138 1月 30 10:06 subprocess_demo.py + +总用量 4 +-rw-rw-r-- 1 hugo hugo 0 1月 30 10:00 __init__.py +-rw-rw-r-- 1 hugo hugo 138 1月 30 10:06 subprocess_demo.py +0 +总用量 4 +-rw-rw-r-- 1 hugo hugo 0 1月 30 10:00 __init__.py +-rw-rw-r-- 1 hugo hugo 138 1月 30 10:06 subprocess_demo.py + +进程已结束,退出代码0 +``` + + +### Allure项目运行 + +在common文件夹下面的Base.py文件中,添加如下代码:(运用我们上一节学习的subprocess模块) + + + +```python +def allure_report(report_path,report_html): + """ + 生成allure 报告 + :param report_path: + :param report_html: + :return: + """ + #执行命令 allure generate + allure_cmd ="allure generate %s -o %s --clean"%(report_path,report_html) + #subprocess.call + log.info("报告地址") + try: + subprocess.call(allure_cmd,shell=True) + except: + log.error("执行用例失败,请检查一下测试环境相关配置") + raise +``` \ No newline at end of file diff --git "a/_posts/2023-01-30-\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\344\273\245\345\217\212\346\265\213\350\257\225\347\224\250\344\276\213\350\256\276\350\256\241\346\200\273\347\273\223.md" "b/_posts/2023-01-30-\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\344\273\245\345\217\212\346\265\213\350\257\225\347\224\250\344\276\213\350\256\276\350\256\241\346\200\273\347\273\223.md" new file mode 100644 index 0000000000..4b5ef5b71a --- /dev/null +++ "b/_posts/2023-01-30-\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\344\273\245\345\217\212\346\265\213\350\257\225\347\224\250\344\276\213\350\256\276\350\256\241\346\200\273\347\273\223.md" @@ -0,0 +1,117 @@ +--- +layout: post +title: "接口自动化测试以及测试用例设计总结" +date: 2023-01-30 +description: "介绍了接口自动化测试框架框架搭建,第三方插件,配置文件设置和读取,测试流程等内容。测试用例设计部分,则包括Excel测试用例,控制流和数据流,方法的应用场景,测试用例书写标准以及测试集的创建。" +tag: 自动化测试 +--- + +介绍了接口自动化测试框架框架搭建,第三方插件,配置文件设置和读取,测试流程等内容。测试用例设计部分,则包括Excel测试用例,控制流和数据流,方法的应用场景,测试用例书写标准以及测试集的创建。 + +# 接口自动化测试总结 + +## 框架搭建 + +* 工具 +* 目录 +* 配置 +* pytest框架 + * pytest.ini + * conftest.py + * 基本命令 + * 三方插件 + +* Yaml配置文件 + * 配置文件 + * 数据驱动 +* Excel + * 测试数据配置 + * 数据驱动 +* 日志 + * 日志配置 + * 日志输出 +* Allure报告 + * 安装 + * 使用 + * 测试用例 +* 邮件配置 + * 邮件配置 + * 邮件发送 +* 接口测试框架流程 + +主程序 -> Excel测试用例 -> Request请求 -> 断言 -> 日志 -> Allure报告 -> 邮件发送 + +## 测试设计 + +### 测试框架的设计 + +#### 测试用例的设计 + +测试用例是为了某个特定的测试目标而设计的,它是测试操作过程序列、条件、期望结果和相关数据的一个特定的集合。 + +从需求出发,设计测试用例,不同的测试数据或测试条件和用户故事(user story)、应用场景叠加,形成测试用例。 + +还可以基于被测系统(system under test)的功能点、业务流程、业务规则、业务数据等,设计测试用例。 + +测试用例和功能结构有对应关系,呈现出层次结构。 + +例如,在“登录”功能模块中,层次结构如下: + +* 用户身份合法性验证(父用例) + * 用户名验证(子用例) + * 正常用户名的输入(孙用例) + * 含有特殊字符的用户名输入 + * 字母大小写无关性测试 + * 非法用户名输入 + * 密码验证(子用例) + * 正常密码 + * 含有特殊字符的密码 + * 字母区分大小写的测试 + * 密码有效期验证 + * 密码保存 + * 忘记密码之后找回密码功能 + +#### Excel测试用例 + +一个完整的Excel测试用例表应该包括以下几个部分: + +用例编号,用例名称,用例类型,用例优先级,用例状态,用例版本,用例创建人,用例创建时间,用例修改人,用例修改时间,用例描述,用例前置条件,用例后置条件,用例步骤,用例预期结果,用例附件,用例备注。 + +#### 控制流和数据流 + +可以从代码、功能点和场景等维度去衡量测试覆盖率,但是场景、功能点具有不确定性,比较确定的测试覆盖率是基于数据分析的数据流和基于逻辑结构的控制流。测试设计方法就是通过测试用例的不断设计、优化,最终达到控制流和数据流的覆盖。 + +在业务层,控制流主要体现在业务流程图,数据流体现在数据流程图。 + +如“用户登录”功能模块的业务流程图: + +!["用户登录"功能模块的业务流程图](/images/posts/2023-01-30-接口自动化测试以及测试用例设计总结/用户登录流程图2.png) + +根据上述流程图来设计测试用例,需要覆盖每个分支和条件,以保证测试的覆盖率。 + +首先,要考虑分成“不能自动登录”和“自动登录”两类;对于“不能自动登录”,要考虑“记住密码”和“不记住密码”两种情况;对于“自动登录”,要考虑“记住密码”和“不记住密码”两种情况。对于不同输入项的错误,可以分成“用户名错误”、“密码错误”、“验证码错误”、“用户名和密码错误”、“用户名和验证码错误”、“密码和验证码错误”、“用户名、密码和验证码错误”等情况。这样,就可以设计出对呀的测试用例。 + +#### 清楚方法的应用场景 + +* 表单中单个输入框的测试属于单因素测试,可以使用等价类划分法、边界值分析法等方法。等价类划分让我们优化测试数据,并关注无效等价类;边界值分析方法则让我们关注边界值。 +* 如果考虑多个条件(条件只包括成立和不成立)组合作用时,这时选用决策表方法。如果很难构造出决策表,可以先用因果图分析,再由因果图产生决策表。 +* 如果不是条件,而是多个因素/变量,每个因素/变量有多个选项(取值),这时要看能否完全组合。如果完全组合数很大,就考虑用两两组合(pairwise)方法或者正交实验方法。 + +### 测试用例书写标准 + +测试用例标准模板的主要元素如下: + +* 标识符 +* 测试项 +* 测试环境要求 +* 输入标准 +* 输出标准 +* 测试用例之间的关联 + +### 测试集的创建 + +测试集是测试用例的集合,可以对测试用例进行分类管理。测试集的创建可以按照模块、功能、功能模块、功能点等方式进行分类。 + + + + diff --git "a/_posts/2023-01-30-\351\202\256\344\273\266\351\205\215\347\275\256.md" "b/_posts/2023-01-30-\351\202\256\344\273\266\351\205\215\347\275\256.md" new file mode 100644 index 0000000000..f4499d0df8 --- /dev/null +++ "b/_posts/2023-01-30-\351\202\256\344\273\266\351\205\215\347\275\256.md" @@ -0,0 +1,133 @@ +--- +layout: post +title: "邮件配置" +date: 2023-01-30 +description: "介绍了邮件配置的目的,邮件配置的方法,邮件封装以及邮件运行" +tag: 自动化测试 +--- + +介绍了邮件配置的目的,邮件配置的方法,邮件封装以及邮件运行。 + +# 邮件配置 + +## 目的 + +使用邮件,方便获取自动化测试的结果 + +## 配置文件设置以及邮件封装 + +### 邮件配置 + +举个例子: + +打开163邮箱或者qq邮箱的设置页面,在设置页面中找到POP3/SMTP/IMAP服务,开启服务(查看服务器地址),获取授权码,填写到配置文件config.yml中 + +```yaml +email: + smtpserver: "smtp.itcast.com" + username: "wangxu@itcast.com" + password: "FDSArewq1" + receiver: "wangxu@itcast.com" +``` + +修改了配置文件后,需要在Conf.py中添加如下代码,来读取email配置有关信息: + +```python +def get_email_info(self): + """ + 获取邮件配置相关信息 + :return: + """ + return self.config["email"] +``` + +### 邮件封装 + +在utils文件夹下新建一个EmailUtil.py文件,用来封装邮件发送的方法 + +```python +from email.mime.multipart import MIMEMultipart # MIMEMultipart类表示邮件的主体,可以包含多个邮件体,可以包含文本,附件等 +from email.mime.text import MIMEText # MIMEText类表示邮件的文本内容 +import smtplib +#初始化 +#smtp地址,用户名,密码,接收邮件者,邮件标题,邮件内容,邮件附件 +class SendEmail: + # __init__方法表示初始化方法,当类被实例化时,会自动调用该方法 + def __init__(self,smtp_addr,username,password,recv, + title,content=None,file=None): + self.smtp_addr = smtp_addr # 邮件服务器地址 + self.username = username + self.password = password + self.recv = recv + self.title = title + self.content = content + self.file = file + #发送邮件方法 + def send_mail(self): + #MIME + msg = MIMEMultipart() + #初始化邮件信息 + msg.attach(MIMEText(self.content,_charset="utf-8")) + msg["Subject"] = self.title + msg["From"] = self.username + msg["To"] = self.recv + #邮件附件 + #判断是否附件 + if self.file: + #MIMEText读取文件 + att = MIMEText(open(self.file).read()) + #设置内容类型 + att["Content-Type"] = 'application/octet-stream' + #设置附件头 + att["Content-Disposition"] = 'attachment;filename="%s"'%self.file + #将内容附加到邮件主体中 + msg.attach(att) + #登录邮件服务器 + self.smtp = smtplib.SMTP(self.smtp_addr,port=25) + self.smtp.login(self.username,self.password) + #发送邮件 + self.smtp.sendmail(self.username,self.recv,msg.as_string()) + +if __name__ == "__main__": + #初始化类(self,smtp_addr,username,password,recv, + # title,content=None,file=None): + from config.Conf import ConfigYaml + email_info = ConfigYaml().get_email_info() + smtp_addr = email_info["smtpserver"] + username = email_info["username"] + password = email_info["password"] + recv = email_info["receiver"] + email = SendEmail(smtp_addr,username,password,recv,"测试") + email.send_mail() +``` + +## 邮件运行 + +这一步主要包括:(1)封装公共方法;(2) 应用测试发送 + +在common文件夹下面的Base.py文件中,添加如下代码: + +```python +def send_mail(report_html_path="",content="",title="测试"): + """ + 发送邮件 + :param report_html_path: + :param content: + :param title: + :return: + """ + email_info = ConfigYaml().get_email_info() + smtp_addr = email_info["smtpserver"] + username = email_info["username"] + password = email_info["password"] + recv = email_info["receiver"] + email = SendEmail( + smtp_addr=smtp_addr, + username=username, + password=password, + recv=recv, + title=title, + content=content, + file=report_html_path) + email.send_mail() +``` \ No newline at end of file diff --git "a/_posts/2023-01-31-vscode+PicGO+Github\346\220\255\345\273\272\344\270\252\344\272\272\345\215\232\345\256\242\345\233\276\345\272\212.md" "b/_posts/2023-01-31-vscode+PicGO+Github\346\220\255\345\273\272\344\270\252\344\272\272\345\215\232\345\256\242\345\233\276\345\272\212.md" new file mode 100644 index 0000000000..529f5ed05d --- /dev/null +++ "b/_posts/2023-01-31-vscode+PicGO+Github\346\220\255\345\273\272\344\270\252\344\272\272\345\215\232\345\256\242\345\233\276\345\272\212.md" @@ -0,0 +1,70 @@ +--- +layout: post +title: "Markdowm插件Office Viewer介绍以及vscode+PicGO+Github搭建个人博客图床" +date: 2023-01-31 +description: "Markdowm插件Office Viewer介绍以及vscode+PicGO+Github搭建个人博客图床" +tag: 个人网站 +--- +Markdowm插件Office Viewer介绍以及vscode+PicGO+Github搭建个人博客图床 + +## Markdowm插件 + +* Markdown All in One +* Office Viewer + + 这个插件真的太棒了,对于md文件就像开了挂一样的好用。安装完之后,会设置好Vditor,它是一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。 它使用 TypeScript 实现,支持原生 JavaScript 以及 Vue、React、Angular 和 Svelte 等框架。 + 稍微体验了一下,有如下优点: + +图片、表格、代码块,列表(有序、无序、任务)、引用、分隔、斜体等傻瓜式操作;方便快速写标题以及大纲视图;导出PDF极其方便;最上方预览小按钮点击之后进入预览界面,可以选择桌面或者手机端浏览,可以把写好的内容直接发布到微信公众号或者知乎;CSDN博客的内容如果是用markdown写的,直接复制进来,格式都不用调整。 + +![office_viewer使用示意图](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/office_viewer使用示意图.jpg) + +## PicGO介绍 + +vscode上有一个picgo插件,可以让我们用快捷键即可上传图片到默认的免费服务器,具体的使用方法是,安装完成后: + +1. Ctrl+Alt+U 从剪切板粘贴图片,上传到服务器; +2. Ctrl+Alt+E 打开资源管理器,选择图片。 + +picgo默认上传的服务器是 `SM.MS`服务器,由于你懂得原因,国内的访问速度让人抓狂,但是picgo支持github以及阿里云作为图床。 + +其中,github图床最为稳定,且一个仓库上限是100G,只做图床绰绰有余。最糟糕的一点是速度,但是使用cdn加速之后速度还是可以的。 + +## Github图床设置 + +1. 打开github,创建新的public仓库,仓库名称可以设置为[PicgoBed](https://github.com/ChanJeunlam/PicgoBed)。 +2. 进入个人设置页面,选择开发者设置 + + ![github_setting](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/github_setting.jpg) +3. 选择Personal access tokens,选择生成新token,此token是图床上传时验证身份用的;添加描述,将repo选上,Expiration选择no expiration + + ![tokens](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/tokens.jpg) +4. 将生成的字符串保存,**关闭页面后将再也无法看到这个字符串了** + +## PicGO设置 + +GitHub设置完之后,我们需要修改picgo插件的设置,把服务器编程我们的图床,具体设置如下: + +1. vscode右下角选择设置,搜索设置里面输入PicGO,打开扩展里面的PicGO +2. 设置如下,github token写我们刚才复制下来的 + + ![picgosetting](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/picgosetting.jpg) + +* `current`设置为GitHub +* `Branch`是我们仓库的分支,默认为 `master` 或者 `main` +* `custom url` 使我们图片上传的连接,有两种方式可以使用 + * 原生方式 + * 使用GitHub原生连接,格式为 `https://raw.githubusercontent.com/用户名/仓库名/分支名` + * 我的例子 `https://raw.githubusercontent.com/ChanJeunlam/PicgoBed` + * 原生方式有一个弊端就是,国内速度比较慢 + * cdn加速方式 + * 格式为 `https://cdn.jsdelivr.net/gh/用户名/仓库名@分支名` + * 我的例子 `https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed` + * cdn加速的优点是国内访问速度比较快 +* `path`是我们的图片存储在仓库中的路径,比如我的是 `blogs/pictures/` +* `Repo`是我们的仓库地址,`Username/Repo`,比如说我的是 ``ChanJeunlam/PicgoBed``. + +其他可能出现的问题的处理: + +* `Branch`里面的main写错成master +* `ChanJeunlam/PicgoBed` 加了空格,写错成 `ChanJeunlam / PicgoBed`,导致上传到服务器失败。 diff --git "a/_posts/2023-01-31-\347\276\216\345\244\232\345\225\206\345\237\216\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\351\241\271\347\233\256\344\273\243\347\240\201\346\241\206\346\236\266.md" "b/_posts/2023-01-31-\347\276\216\345\244\232\345\225\206\345\237\216\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\351\241\271\347\233\256\344\273\243\347\240\201\346\241\206\346\236\266.md" new file mode 100644 index 0000000000..d26f88d576 --- /dev/null +++ "b/_posts/2023-01-31-\347\276\216\345\244\232\345\225\206\345\237\216\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\351\241\271\347\233\256\344\273\243\347\240\201\346\241\206\346\236\266.md" @@ -0,0 +1,361 @@ +--- +layout: post +title: "美多商城接口自动化测试项目代码框架" +date: 2023-01-31 +description: "美多商城接口自动化测试项目代码框架" +tag: 自动化测试 +--- + +![美多商城接口自动化测试项目框架代码](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/美多商城接口自动化测试项目框架代码.png) + +## utils + +### AssertUtil.py + +- AssertUtil类(断言封装) + + - assert_code方法 + +验证返回状态码 + +assert int(code) == int(expected_code) + + - assert_body方法 + +验证返回结果内容相等 + +assert body == expected_body + + - assert_in_body方法 + +验证返回结果是否包含期望的结果 + +assert expected_body in body + +### EmailUtil.py + +- SendEmail类 + + - __init__方法:初始化,当类被实例化之后,会获得smtp地址,用户名,密码,接收邮件者,邮件标题,邮件内容,邮件附件等信息 + - send_mail方法 + + - 初始化邮件信息 + - 判断是否有附件 + - MIMEText读取文件、设置内容类型、设置附件头 + - 将内容附加到邮件主体中 + - 登录邮件服务器 + - 发送邮件 + +### ExcelUtil.py + +- ExcelReader类 + + - data方法:读取sheet方式,名称,索引 + +### LogUtil.py + +- Logger类 + + - 输出控制台 + - 输出文件 +- my_log:对外方法,初始log工具类,提供其它类使用 + +### MysqlUtil.py + +大多数情况下,我们接口自动化测试需要对数据库进行验证,比如说,我们在注册接口中,需要验证数据库中是否有新增的数据,这时候我们就需要对数据库进行查询,然后对查询结果进行断言。比如说,我们需要对订单接口进行测试,需要验证数据库中的订单数据是否正确,这时候我们就需要对数据库进行查询,然后对查询结果进行断言。 + +- Mysql封装类 + + - __init__方法:链接数据库,光标对象 + - fetchone方法:单个查询 + - fetchall方法:多个查询 + - exec方法:执行数据库,比如说增删查改 + +### RequestsUtil.py + +- Request类 + + - get方法 + - post方法 + +### YamlUtil.py + +- YamlReader类 + + - data方法:单个文件读取 + - data_all方法:多个文档读取 + +## config + +### Conf.py + +- 获取文件/目录路径方法 + + - report路径 + - 测试数据data路径 + - db_conf.yml路径 + - config目录的路径 + + - conf.yml文件的路径 + - Log文件路径 +- ConfigYaml类 + + - get_excel_file:获取测试用例excel名称方法 + - get_excel_sheet:获取测试用例sheet名称方法 + - get_conf_url:获取接口地址方法 + - get_conf_log:获取日志级别方法 + - get_conf_log_extension:获取文件扩展名方法 + - get_db_conf_info:根据db_alias获取该名称下的数据库信息方法 + - get_email_info:获取邮件配置相关信息 + +### conf.yml + +- BASE + + - log_level + + - debug:默认 + - info + - warning + - error + - log_extension + - test + + - url + - cae_file + - case_sheet +- email + + - smtpserver + - username + - password + - receiver + +### db_conf.yml + +- dh_1 + + - dh_host + - dh_user + - dh_password + - dh_name + - dh_charset + - dh_port +- dh_2 + + - ... +- dh_3 + + - ... + +## common + +### Base.py + +- init_db方法:初始数据化信息,初始化mysql对象 +- assert_db方法:数据库的结果与接口返回的结果验证 +- json_parse方法:格式化字符,转换json +- res_find方法:查询符合'\${(.*)}\$'的所有字符串 +- res_sub方法:替换字符串 +- params_find方法:验证请求中是否有${}$需要结果关联 +- allure_report方法:生成allure 报告 + + - #执行命令 allure generate + allure_cmd ="allure generate %s -o %s --clean"%(report_path,report_html) + - #subprocess.call + subprocess.call(allure_cmd,shell=True) +- send_mail方法 + + - 使用ConfigYaml().get_email_info()获得email信息 + - 使用SendEmail类的send_mail方法发送邮件 + +### ExcelConfig.py + +- DataConfig类 + + - 包括用例ID,模块,接口名称,请求URL,前置条件,请求类型,请求参数类型,请求参数 预期结果,实际结果,备注,是否运行,headers,cookies,status_code,数据库验证等等 + +### ExcelData.py + +- Data类 + + - get_run_data方法: + 获取全部测试用例 + - get_run_data方法: + 根据是否运行列==y,获取执行测试用例 + - get_case_pre方法: + 根据前置条件:从全部测试用例取到测试用例 + +## data + +### testdata.xlsx + +- 登录 + + - 请求方式:POST/authorizations + - 请求参数:1. username 2. password + 返回数据:1. username 2. user_id 3. token(身份认证凭据) + + - 空白 + + - 无效数据。期待为字典类型 + - {"username":"","password":"12345678"} + + - username': ['该字段不能为空。'] + - {"username":"","password":""} + + - password': ['该字段不能为空。'] + - {"username":"python","password":"12345678"} + + - user_id': 1, 'username': 'python' + - {"username":"python","password":"test123"} + + - 无法使用提供的认证信息登录 +- 用户中心个人信息 + + - 请求方式: GET /user/ + - 返回数据:id username mobile email email_active + + - 身份认证信息未提供 + - id': 1, 'username': 'python', 'mobile': '17701397029', 'email': '952673638@qq.com' +- 获取商品列表数据 + + - 请求方式: GET `/categories/(?P\d+)/skus?page=xxx&page_size=xxx&ordering=xxx` + - 请求参数:categroy_id,page,page_size,ordering + - 返回参数:id name price default_image_url(默认图片) comments +- 添加到购物车 + + - 请求方式 : POST /cart/ + - 请求参数:sku_id(商品sku id) count(数量)selected(是否勾选,默认勾选) + - 返回数据:sku_id count selected +- 保存订单 + + - 请求方式 : POST /orders/ + - 请求参数:address pay_method + - 返回数据:order_id 订单编号 + +### testlogin.yml + +- case_name:测试用例名称 +- "url": "/authorizations/" +- data + + - username + - password +- expect: 期望结果 + +## logs + +### 日志文件名称 + +日志文件名称= logs目录 + 当前时间+扩展名 + +### 日志文件内容 + +'%(asctime)s %(name)s %(levelname)s %(message)s ' + +## report + +### report.html + +### result + +- report.html + +## run and requirement + +### pytest.ini + +- addopts :报告生成地址 +- testpaths = testcase # 测试路径:testcase文件夹 +- python_files = test_*.py # testcase文件夹下面test开头的py文件 +- python_classes = Test_* # Test开头的类 +- python_functions = test_* # test开头的函数 + +### requirements.txt + +- pip install pipreqs # 安装pipreqs +- pipreqs ./ # 生成requirements.txt +- pip install -r requirements.txt # 安装项目依赖的包 + +### run.py执行测试用例 + +- report_path + report_html_path + pytest.main(["-s","--alluredir",report_path]) + Base.send_mail() + +## testcase + +### t_log + +- log_demo.py:设置配置信息;定义日志名称getlogger;设置日志级别 +- log_file_demo.py:设置log级别,handler,日志级别,输出格式,将日志等写入文件 + +### t_allure + +- allure_demo.py + + - @allure.feature一级标签 + - @allure.title测试用例标题 + - @allure.description执行测试用例结果的描述 + - @allure.story二级标签 + - allure.dynamic多个输入结果 + +### t_pytest + +- pytest_assert.py断言测试 +- pytest_class.py + + - setup_class在类中所有方法执行前执行 + - teardown_class在类中所有方法执行后执行 +- pytest_demo.py + + - 断言的方法 + - @pytest.mark.flaky(reruns=3,reruns_delay=2) #失败重跑3次,每次间隔2秒 + flaky插件表示失败重跑 + +### t_excel + +- excel_demo.py:获取testdata.xlsx行列信息 + +### t_yml + +- yaml_demo.py + + - yaml.safe_load:使用yaml读取文件 + - yaml.safe_load_all: 读取多个文档 + +### t_subprocess + +- subprocess_demo.py + + - shell=True 会调用shell + +### test_login.py + +- 获取测试用例内容list + "case_name": "登录成功用例" + "case_name": "登录失败用例" +- 参数化执行测试用例 + +### test_excel_case.py + +- TestExcel类 + + - run_api方法发送请求API + - run_pre方法初始化数据 + - test_run方法 + + - 验证前置条件 + - 找到执行用例 + - allure创建执行报告 + - 断言验证 + +### test_Mall.py + +- test_login方法:登录测试 +- test_info方法:个人信息 +- goods_list方法:商品列表数据 +- cart方法:添加购物车 +- order方法:保存购物车 diff --git "a/_posts/2023-02-08-python\346\225\264\346\264\201\344\271\213\351\201\223.md" "b/_posts/2023-02-08-python\346\225\264\346\264\201\344\271\213\351\201\223.md" new file mode 100644 index 0000000000..5b9ca329f9 --- /dev/null +++ "b/_posts/2023-02-08-python\346\225\264\346\264\201\344\271\213\351\201\223.md" @@ -0,0 +1,219 @@ +--- +layout: post + +title: "Python整洁之道" + +date: 2023-02-08 + +description: "主要包括Python中的断言使用,逗号放置,with语句和上下文管理器" + +tag: Python +--- +# 断言 + +断言是为了告诉开发人员程序中发生了不可恢复的错误。对于可以预料的错误(如未找到相关文件),用户可以予以纠正或重试,断言并不是为此而生的。 + +断言用于程序内部自检,如声明一些代码中不可能出现的条件。如果触发了某个条件,即意味着程序中存在相应的 bug。 + +## 断言语法 + +```python +assert_stmt ::="assert" expression1 ["," expression2] +``` + +其中,expression1 是需要测试的条件,可选的 expression2 是错误消息,如果断言失败则显示该消息。 + +","表示逗号,expression1 和 expression2 之间用逗号分隔。 + +在执行时,Python 解释器将每条断言语句大致转换为以下这些语句: + +```python +if __debug__: + if not expression1: + raise AssertionError(expression2) +``` + +代码在检查断言条件之前,还会检查__debug__全局变量。这是一个内置的布尔标记,在一般情况下为真,若进行代码优化则为假。还可以使用 expression2 传递一个可选的错误消息,该消息将与回溯中的AssertionError 一起显示,用来进一步简化调试。 + +## 断言注意事项 + +### 不要使用断言验证数据 + +在 Python 中使用断言时要注意的一个重点是,若在命令行中使用-O 和-OO 标识,或修改CPython中的 PYTHONOPTIMIZE 环境变量,都会全局禁用断言。 + +不要使用断言验证数据,因为当全局禁用断言时,验证失效,可能存在严重的安全漏洞。 + +### 永不失败的断言 + +在将一个元组作为 assert 语句中的第一个参数传递时,断言条件总为真,因此永远不会失败。 + +```python +assert(1 == 2, 'This should fail') +``` + +这是因为在 Python 中非空元组总为真值。如果将元组传递给 assert 语句,则会导致断言条件始终为真,因此上述 assert 语句毫无用处,永远不会触发异常。 + +这也是为什么应该总是对单元测试用例先做一个快速的冒烟测试。要确保在编 +写下一个测试之前,当前测试用例的确会失败。 + +### 断言总结 + +1. Python 断言语句是一种测试某个条件的调试辅助功能,可作为程序的内部自检 +2. 断言应该只用于帮助开发人员识别 bug,它不是用于处理运行时错误的机制。 + +# 巧妙放置逗号 + +如果需要在 Python 中的列表、字典或集合常量中添加或移除项,记住一个窍门:在所有行后面都添加一个逗号。 + +如,假设代码中有如下名字组成的列表: + +```python +names = ['Alice', 'Bob', 'Cindy', 'Doug'] +``` + +在修改这个名字列表时,通过 git diff 查看改动可能有点不方便。大多数源码控制系统都是基于行的,因此无法标出同一行中的多个改动。 + +一个快速改进是根据编码规范,将列表、字典或集合常量分割成多行,如下所示: + +```python +names = [ + 'Alice', + 'Bob', + 'Cindy' + 'Doug' +] +``` + +在列表末尾添加或移除内容时Jane,需要在'Doug'后面添加逗号,再添加或移除新的名字。否则,Python就会将字符串 Doug 和 Jane 合并成了 DougJane。这称为**字符串字面值**拼接,是文档中有记录的刻意行为,这种行为可能会在程序中引入令人难以琢磨的 bug。 + +在某些情况下,字符串字面值拼接是一个有用的特性。例如,在跨越多行的长字符串中可以省去反斜杠: + +```python +my_str = ( + 'This is a very long string that I want to split ' + 'across multiple lines.' +) +``` + +但另一方面,这个特性有时又会成为负担。那么如何解决这个问题呢? + +在 Python 中,可以在列表、字典和集合常量中的每一项后面都放置一个逗号,包括最后一 +项。因此只要记住在每一行末尾都加上一个逗号,就可以避免逗号放置问题。 + +# 上下文管理器和 with 语句 + +with 语句究竟有哪些好处?它有助于简化一些通用资源管理模式,抽象出其中的功能,将 +其分解并重用。 + +如使用 with 语句打开文件,不需要再手动关闭文件,因为 with 语句会在代码块执行完毕后自动关闭文件。 + +```python +with open('hello.txt','w') as f: + f.write('hello world") +``` + +上面的代码如果不适用 with 语句,就需要手动关闭文件,如下所示: + +```python +f = open('hello.txt','w') +try: + f.write('hello world') +finally: + f.close() +``` + +如果只执行上述代码中的核心逻辑,那么这段代码就很简单。但是,如果在调用 f.write()时发生异常,这段代码不能保证文件最后被关闭,因此程序可能会泄露文件描述符。此时 with 语句就派上用场了,它能够简化资源的获取和释放。 + +```python +f = open('hello.txt','w') +f.write('hello world') +f.close() +``` + +threading.Lock 类是 Python 标准库中另一个比较好的示例,它有效地使用了 with 语句。Lock 类的实例可以用作上下文管理器,因此可以使用 with 语句来获取和释放锁。 + +```python +lock = threading.Lock() +# have problems +lock.acquire() +try: + print('Critical section 1') + print('Critical section 2') +finally: + lock.release() + +``` + +改写代码后,使用 with 语句都可以抽象出大部分资源处理逻辑。不再需要手动调用 lock.release(),因为 with 语句会在代码块执行完毕后自动释放锁。with 语句不仅让处理系统资源的代码更易读,而且由于绝对不会忘记清理或释放资源,因此还可以避免 bug 或资源泄漏。 + +```python +lock = threading.Lock() +# have problems +lock.acquire() + +# use with +with lock: + print('Critical section 1') +``` + +## 在自定义对象中支持 with + +只要实现所谓的上下文管理器(context manager),就可以在自定义的类和函数中获得相同的功能。 + +上下文管理器是什么?这是一个简单的“协议”(或接口),自定义对象需要遵循这个接口来支持 with 语句。总的来说,如果想将一个对象作为上下文管理器,需要做的就是向其中添加__enter__和__exit__方法。Python 将在资源管理周期的适当时间调用这两种方法。 + +下面是open()上下文管理器的实现,它是一个内置的函数,用于打开文件并返回一个文件对象。 + +```python + +class OpenFile(object): + def __init__(self, file, mode='r'): + self.file = file + self.mode = mode + + def __enter__(self): + self.f = open(self.file, self.mode) + return self.f + # __exit__方法的参数是异常类型、异常值和追溯信息,type、value和traceback + def __exit__(self, type, value, traceback): + self.f.close() +``` + +这里的OpenFile类遵循上下文管理器协议,也支持 with 语句。因此,可以使用 with 语句来打开文件,而不需要手动关闭文件。 + +```python +with OpenFile('hello.txt','w') as f: + f.write('hello world') +``` + +当执行流程进入 with 语句上下文时,Python 会调用__enter__获取资源;离开 with 上下文时,Python 会调用__exit__释放资源。 + +使用contextlib.contextmanager装饰器,可以简化上下文管理器的实现。contextmanager装饰器会将生成器函数包装成上下文管理器。这样,就可以使用 with 语句来管理资源,而不需要实现__enter__和__exit__方法。 + +```python +from contextlib import contextmanager + +@contextmanager +def OpenFile(file,mode): + try: + f = open(file,mode) + yield f # yield 语句会在上下文管理器中暂停执行,直到离开上下文管理器 + finally: + f.close() + +with OpenFile('hello.txt','w') as f: + f.write('hello world') +``` + +这个 managed_file()是生成器,开始先获取资源,之后暂停执行并产生资源以供调用者使用。当调用者离开 with 上下文时,生成器继续执行剩余的清理步骤,并将资源释放回系统。 + + +总结: + +1. with语句通过在所谓的上下文管理器中封装 try...finally 语句的标准用法来简化异常处理; +2. with 语句一般用来管理系统资源的安全获取和释放。资源首先由 with 语句获取,并在执行离开 with 上下文时自动释放; +3. 有效地使用 with 有助于避免资源泄漏的问题,让代码更加易于阅读。 + +# 来源 + +《深入理解Python特性》P04 - P15 diff --git "a/_posts/2023-02-12-JMeter \345\256\211\350\243\205\345\217\212\345\205\245\351\227\250.md" "b/_posts/2023-02-12-JMeter \345\256\211\350\243\205\345\217\212\345\205\245\351\227\250.md" new file mode 100644 index 0000000000..18783c5a9f --- /dev/null +++ "b/_posts/2023-02-12-JMeter \345\256\211\350\243\205\345\217\212\345\205\245\351\227\250.md" @@ -0,0 +1,116 @@ +--- +layout: post + + +title: "JMeter 安装及入门" + + +date: 2023-02-12 + + +description: "主要包括JMeter安装步骤以及基本使用方法介绍" + + +tag: 自动化测试 +--- +# JMeter安装 + +## 安装Java + +JMeter是基于Java开发的,所以需要先安装Java环境,包括JDK,JAVA开发工具包和JRE,Java运行环境。JDK是Java开发工具包,包含了Java的开发工具,如:Java编译器、Java文档生成器、Java调试器等。JDK是Java开发的基础,JRE是Java运行的基础,JVM是Java运行的平台。 + +下载地址:[Java 19 and Java 17 available now +](https://www.oracle.com/cn/java/technologies/downloads/#java16) + +下载选择Linux x64版本(x64 Compressed Archive),下载完成后,解压到指定目录,如:/usr/local + +```bash +sudo tar -zxvf jdk-19_linux-x64_bin.tar.gz -C /usr/local # -C 指定解压目录 +``` + +### 修改环境变量 + +```bash +sudo vim /etc/profile +``` + +在/etc/profile文件末尾添加以下内容: + +```bash +JAVA_HOME=/usr/local/jdk-19.0.2 # JDK安装目录 +CLASSPATH=.:$JAVA_HOME/lib.tools.jar # 这个变量设置的目的是为了程序能找到相应的被执行的".class"文件 +PATH=$PATH:$JAVA_HOME/bin # BIN是JDK的可执行文件目录 +export JAVA_HOME CLASSPATH PATH +``` + +重新加载/etc/profile配置文件 + +```bash +source /etc/profile +``` + +### 验证Java安装 + +```bash +java -version # +``` + +```bash +javac -version +``` + +## 安装JMeter + +[Apache JMeter 5.5安装地址](https://jmeter.apache.org/download_jmeter.cgi),选择Binaries中的apache-jmeter-5.5.tgz下载。 + +如果觉得速度太慢,可以切换到国内的[阿里云镜像](http://mirrors.aliyun.com/apache/jmeter/binaries/?spm=a2c6h.25603864.0.0.5da369a5TPLRdD)下载 + +解压缩 + +```bash +tar -zxvf apache-jmeter-5.5.tgz +``` + +### 配置JMeter环境变量 + +```bash +sudo vim /etc/profile +``` + +在/etc/profile文件末尾添加以下内容: + +```bash +export JMETER_HOME=/usr/local/apache-jmeter-5.5 # JMeter安装目录 +export PATH=$JMETER_HOME/bin:$PATH +``` + +重新加载/etc/profile配置文件 + +```bash +source /etc/profile +``` + +### 验证JMeter安装 + +```bash +jmeter -v +``` + +运行JMeter + +```bash +jmeter +``` + +# JMeter入门 + +## 设置 + +修改语言为中文,如下图所示,打开options,选择choose Language,选择Chinese(simplified) +![修改语言为中文](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230212140228.png) + +防止中文乱码,打开jmeter.properties文件,(位于apache-jmeter-5.5/bin目录下),修改以下内容: + +```bash +sampleresult.default.encoding=UTF-8 +``` diff --git "a/_posts/2023-02-18-Linux\344\270\213Flatpak\347\232\204\345\256\211\350\243\205\344\270\216\344\275\277\347\224\250\357\274\210\344\273\245\345\256\211\350\243\205OBS\344\270\272\344\276\213).md" "b/_posts/2023-02-18-Linux\344\270\213Flatpak\347\232\204\345\256\211\350\243\205\344\270\216\344\275\277\347\224\250\357\274\210\344\273\245\345\256\211\350\243\205OBS\344\270\272\344\276\213).md" new file mode 100644 index 0000000000..70d25db023 --- /dev/null +++ "b/_posts/2023-02-18-Linux\344\270\213Flatpak\347\232\204\345\256\211\350\243\205\344\270\216\344\275\277\347\224\250\357\274\210\344\273\245\345\256\211\350\243\205OBS\344\270\272\344\276\213).md" @@ -0,0 +1,70 @@ +--- +layout: post + +title: "Linux下Flatpak的安装与使用(以安装OBS为例)" + +date: 2023-02-18 + +description: "Linux下Flatpak的安装与使用(以安装OBS为例)" + +tag: Linux +--- +软件包管理是任何Linux发行版的重要功能之一,可简化Linux应用程序的安装和维护方法。不同的Linux发行版采用不同的方法来打包和分发软件。 + +但是对于某些切换到不同的Linux发行版来的人说,相同的功能有时反而会成为绊脚石。他们发现很难理解新的软件包管理器,并且无法安装应用程序。为了使用多个程序包管理器解决此类问题,Linux发行版已经发展出了通用的包管理系统,如Snap、Appimage和Flatpak。 + +# 什么是Flatpak? + +Flatpak是一个通用的软件包管理系统,用于在任何Linux发行版上构建和分发应用程序。您无需学习特定于发行版的软件包管理器即可安装Flatpak应用。它为所有Linux发行版提供了一个命令行实用程序,以下载,安装和更新该应用程序。 + +# 什么是Flathub? + +Flathub 是一个包含了几乎所有 flatpak 应用的仓库,可为Linux系统提供大量的应用程序和游戏。它还为想要构建,分发和定期更新应用程序的开发人员提供了构建服务。 + +# 设置Flatpak + +```bash +# 安装flatpak +sudo apt install flatpak +# 安装图形化界面,但是我没有安装成功 +sudo apt install gnome-software-plugin-flatpak +# 添加flathub仓库 +flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo +``` + +# 从Flathub商店下载和安装Flatpak应用程序 + +## 安装和运行OBS + +### 从Flathub商店下载 + +* 打开[flathub](https://flathub.org/home)网站,搜索OBS,点击安装 +* 这时候,com.obsproject.Studio.flatpakref会被安装到/home/hugo/下载路径下 +* 安装flatpakref后缀文件 + * ```bash + flatpak install --from com.obsproject.Studio.flatpakref + ``` +* 安装完成后,可以查找到OBS + * ```bash + flatpak list # 查看OBS的app id + ``` +* 运行OBS + * ```bash + flatpak run + ``` + +### 搜索安装 + +```bash +flatpak search obs +flatpak install +flatpak run +``` + +### 查看安装结果 + +```bash +flatpak list +``` + +![已经安装的应用](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230218164328.png) diff --git "a/_posts/2023-02-18-Python\345\255\227\347\254\246\344\270\262\346\240\274\345\274\217\345\214\226.md" "b/_posts/2023-02-18-Python\345\255\227\347\254\246\344\270\262\346\240\274\345\274\217\345\214\226.md" new file mode 100644 index 0000000000..a38ae76165 --- /dev/null +++ "b/_posts/2023-02-18-Python\345\255\227\347\254\246\344\270\262\346\240\274\345\274\217\345\214\226.md" @@ -0,0 +1,81 @@ +--- +layout: post + +title: "Python字符串格式化" + +date: 2023-02-18 + +description: "主要包括Python中的字符串格式化" + +tag: Python +--- +字符串是指由零个或多个字符组成的有限序列,是编程语言中最常用的数据类型之一。字符串是由单引号或双引号括起来的任意文本,比如'abc',"xyz"等等。请注意,''或""本身只是一种表示方式,不是字符串的一部分,因此,字符串'abc'只有a,b,c这3个字符。 + +字符串的格式化是指在字符串中插入变量,格式化字符串的输出方式有很多种,比如通过%操作符,format方法,字符串字面值插值等。 + +# 通过%操作符格式化 + +```python +'Hell %s' % 'world' +``` + +%s 表示字符串,%d 表示整数,%f 表示浮点数,%x 表示十六进制整数。 + +```python +# 多个%操作符 +'we have bought %d apples, %d oranges and %d pears' % (3, 5, 2) +``` + +```python +'we have %d apples, and the apples come from %s' % (3, 'China') +``` + +# 通过format方法格式化 + +```python +'Hello {0},welcome to {1}.'.format('Jack','China') # 0表示第一个参数,1表示第二个参数 +``` + +你还可以用别名以任意顺序替换变量。这是一个非常强大的功能,不必修改传递给格式函数的参数就可以重新排列显示顺序。 + +```python +'Hey {name}, welcome to {country}.'.format(country='China',name='Jack') +``` + +# 字符串字面值插值 + + +Python 3.6 增加了另一种格式化字符串的方法,称为格式化字符串字面值(formatted string literal)。采用这种方法,可以在字符串常量内使用嵌入的 Python 表达式。 + +```python +a = 3 +b = 5 +f'we have bought {a} apples, {b} oranges and now the total fruit number is {a+b}.' +``` + +本质上,格式化字符串字面值是 Python 解析器的功能:将 f 字符串转换为一系列字符串常量和表达式,然后合并起来构建最终的字符串。 + +这种方法的优点是,它不仅更简洁,而且还更快。因为它不需要调用任何函数,而是由解析器在编译时处理。 + +在函数中使用该格式化语法,如下: + +```python +def greet(name, question): + return f"Hello, {name}! How's it {question}?" +greeter = greet('Jack', 'going') +print(greeter) +``` + +# 模板字符串 + +其他字符串格式化技术所用的语法更复杂,因而可能会给程序带来安全漏洞。因此,Python 3.6 引入了一种新的字符串格式化方法,称为模板字符串(template string)。 + +```python +from string import Template +t = Template('we have bought $num1 apples, $num2 oranges and now the total fruit number is $total.') +t.substitute(num1=3,num2=5,total=8) +``` + +# 来源 + +《深入理解Python特性》P022 - P027 diff --git "a/_posts/2023-02-18-Python\347\261\273\344\270\216\351\235\242\345\220\221\345\257\271\350\261\241.md" "b/_posts/2023-02-18-Python\347\261\273\344\270\216\351\235\242\345\220\221\345\257\271\350\261\241.md" new file mode 100644 index 0000000000..7c70a0220e --- /dev/null +++ "b/_posts/2023-02-18-Python\347\261\273\344\270\216\351\235\242\345\220\221\345\257\271\350\261\241.md" @@ -0,0 +1,165 @@ +--- +layout: post + +title: "Python中的类和面向对象:__repr__和__str__" + +date: 2023-02-18 + +description: "主要包括Python中的类和面向对象" + +tag: Python +--- +# 字符串转换(每个类都需要__repr__) + +在 Python 中定义一个自定义类之后,如果尝试在控制台中输出其实例或在解释器会话中查看,并不能得到十分令人满意的结果。默认的“转换成字符串”功能非常原始,缺少细节: + +```python +class Person: + def __init__(self, name, age): + self.name = name + self.age = age +Person_Bob = Person('Bob', 35) +print(Person_Bob) +``` + +输出结果: + +```console +<__main__.Person object at 0x0000015BC9246640> +``` + +可以看到,print只显示类名和对象实例id的字符串(这是CPython中对象的内存地址)。 + +如何查看具体的内容? + +1. 直接打印类的属性 +2. 向类中加入自定义的to_string()方法来绕过这个问题。 + +```python +class Person: + def __init__(self, name, age): + self.name = name + self.age = age + def to_string(self): + return f'Person(name={self.name}, age={self.age})' +Person_Bob = Person('Bob', 35) +print(Person_Bob.to_string()) +``` + +输出结果: + +```console +Person(name=Bob, age=35) +``` + +但是,这样做有一个问题,如果我们想要打印一个列表中的所有对象,那么就需要一个一个地调用to_string()方法。这样做显然不够优雅。 + +或者,直接打印类的属性,还是不够优雅: + +```python +class Person: + def __init__(self, name, age): + self.name = name + self.age = age +Person_Bob = Person('Bob', 35) +print(Person_Bob.name) +print(Person_Bob.age) +``` + +可以向类中添加双下划线方__str__和__repr__。这两个方法以具有 Python 特色的方式在不同情况下将对象转换为字符串。 + +```python +class Person: + def __init__(self, name, age): + self.name = name + self.age = age + def __str__(self): + return f'Person(name={self.name}, age={self.age})' + +Person_Bob = Person('Bob', 35) +print(Person_Bob) +Person_Bob + +``` + +输出结果: + +```console +Person(name=Bob, age=35) +Out[6]: <__main__.Person at 0x15bc9249250> +``` + +可以看到,在控制台中查看 Car 对象得到的依然是之前包含对象 id 的结果,但是打印对象就会得到由新添加的__str__方法返回的字符串。 + +现在,我们可以格式化字符串,而不必担心对象的内部细节: + +```python +f'{Person_Bob}' +'{0}'.format(Person_Bob) +``` + +## __repr__和__str__的区别 + +__repr__和__str__的区别在于,__str__是面向用户的,而__repr__是面向开发者的。__repr__的目的是为了让开发者能够更容易地理解对象的内容,而__str__的目的是为了让用户能够更容易地理解对象的内容。 + +我们在类中定义了__str__方法,在解释器会话中查看Person_Bob对象时,返回结果依然是<__main__.Person at 0x15bc9249250>。这是因为解释器会话中的对象是__repr__方法返回的字符串,而不是__str__方法返回的字符串。 + +```python +class Car: + def __init__(self,color,mileage): + self.color = color + self.mileage = mileage + def __str__(self): + return f'a {self.color} car' + def __repr__(self): + return f'Car(color={self.color}, mileage={self.mileage})' +my_car = Car('red', 37281) +my_car # 在解释器会话中查看对象,返回的是__repr__方法的结果 +# Out[8]: Car(color=red, mileage=37281) +print(my_car) # 在控制台中查看对象,返回的是__str__方法的结果 +# a red car +'{0}'.format(my_car) # 使用format函数,返回的是__str__方法的结果 +# a red car +``` + +从中可以看出,在 Python 解释器会话中查看对象得到的是对象的__repr__结果。 + +有趣的是,像列表和字典这样的容器总是使用__repr__的结果来表示所包含的对象,哪怕对容器本身调用 str()也是如此。 + +为了手动选择其中一种字符串转换方法,最好使用内置的str()和 repr()函数。 + +在 date 对象上调用 str()时,得到的是一些看起来像 ISO 日期格 +式的东西;__repr__侧重的则是得到无歧义的结果,此在对象上调用 repr()会得到相对更复杂的结 +果,其中甚至包括完整的模块和类名称: + +```python +import datetime +today = datetime.date.today() +# Out[22]: '2023-02-18' +str(today) +# Out[23]: 'datetime.date(2023, 2, 18)' +repr(today) +``` + +复制并粘贴由这个__repr__返回的字符串,可以作为有效的 Python 语句重新创建原 date对象。 + +## 为什么每个类都需要__repr__ + +如果没有定义__repr__方法,那么Python会使用默认的__repr__方法,这个方法返回的是对象的内存地址,这对于调试来说是非常不方便的。因此建议总是为自定义类添加__repr__方法!!! + +```python +class Car: + def __init__(self,color,mileage): + self.color = color + self.mileage = mileage + def __repr__(self): + return f'Car({self.color!r}, {self.mileage!r})' +``` + +这里使用!r 转换标志来确保输出字符串使用的是 repr(self.color)和 repr(self. mileage),而不是 str(self.color)和 str(self.mileage)。 + + + +# 来源 + +《深入理解Python特性》P051 - P058 diff --git "a/_posts/2023-02-18-python\347\211\271\346\200\247-\344\270\213\345\210\222\347\272\277\347\232\204\344\275\277\347\224\250.md" "b/_posts/2023-02-18-python\347\211\271\346\200\247-\344\270\213\345\210\222\347\272\277\347\232\204\344\275\277\347\224\250.md" new file mode 100644 index 0000000000..15bd37e4d4 --- /dev/null +++ "b/_posts/2023-02-18-python\347\211\271\346\200\247-\344\270\213\345\210\222\347\272\277\347\232\204\344\275\277\347\224\250.md" @@ -0,0 +1,87 @@ +--- +layout: post + +title: "Python特性:下划线的使用" + +date: 2023-02-18 + +description: "主要包括Python中下划线" + +tag: Python +--- +# 下划线、双下划线 + +单下划线和双下划线在 Python 变量名和方法名中都有各自的含义。有些仅仅是作为约定,用于提示开发人员;而另一些则对 Python 解释器有特殊含义。 + +## 前置单下划线:_var + +前置单下划线只有约定含义。它对于程序员而言是一种提示。提示其他程序员,以单下划线开头的变量或方法只在内部使用。**PEP8**中定义了这个约定。 + +Python 在“私有”和“公共”变量之间并没有很强的区别。 + +```python +class Test: + def __init(self): + self.foo = 11 + self._bar = 23 +``` + +如果实例化这个类并尝试访问在__init__构造函数中定义的 foo 和_bar 属性,会发生什么情况? + +```python +t = Test() +print(t.foo) # 11 +print(t._bar) # 23 +``` + +可以看到,__bar 前面的单下划线并没有阻止我们“进入”这个类访问变量的值。 + +## 后置单下划线:var_ + +有时,某个变量最合适的名称已被 Python 语言中的关键字占用。在这种情况下,可以追加一个下划线来绕过命名冲突。 + +这是PEP8中的约定。 + +## 前置双下划线:__var + +双下划线前缀会让 Python 解释器重写属性名称,以避免子类中的命名冲突。 + +这也称为名称改写(name mangling),即解释器会更改变量的名称,以便在稍后扩展这个类时避免命名冲突。 + +```python +class Test: + def __init__(self): + self.__baz = 23 +t = Test() +print(t.__baz) # AttributeError: 'Test' object has no attribute '__baz' +dir(t) # ['_Test__baz'] +print(t._Test__baz) # 23 +class Test2(Test): + def __init__(self): + super().__init__()# super() 函数是用于调用父类(超类)的一个方法. + self.__baz = 42 +t2 = Test2() +print(t2._baz) +dir(t2) +print(t2._Test__baz) # 23 +print(t2._Test2__baz) # 42 + +``` + +## 前后双下划线:__var__ + +如果名字前后都使用双下划线,则不会发生名称改写;前后由双下划线包围的变量不受 Python 解释器的影响。 + +同时具有前后双下划线的名称在 Python 中有特殊用途。比如说,__init__这样的对象构造函数;用来让对象可调用的__call__函数。 + +这些双下划线方法通常被称为魔法方法(magic methods),因为它们的名字都是以双下划线开头和结尾的。 + +## 单下划线:_ + +单下划线是一个有效的变量名,用作临时或无意义变量的名称。 + +在 Python 交互式解释器中,单下划线是最后一个表达式的值。 + +# 来源 + +《深入理解Python特性》P015 - P022 diff --git "a/_posts/2023-02-20-\344\275\277\347\224\250\344\270\200\347\273\264\345\215\267\347\247\257\347\245\236\347\273\217\345\257\271\351\243\216\345\212\233\345\217\221\347\224\265\346\234\272\347\273\204\351\253\230\351\200\237\350\275\264\346\211\277\345\257\277\345\221\275\350\277\233\350\241\214\351\242\204\346\265\213.md" "b/_posts/2023-02-20-\344\275\277\347\224\250\344\270\200\347\273\264\345\215\267\347\247\257\347\245\236\347\273\217\345\257\271\351\243\216\345\212\233\345\217\221\347\224\265\346\234\272\347\273\204\351\253\230\351\200\237\350\275\264\346\211\277\345\257\277\345\221\275\350\277\233\350\241\214\351\242\204\346\265\213.md" new file mode 100644 index 0000000000..30d5dea424 --- /dev/null +++ "b/_posts/2023-02-20-\344\275\277\347\224\250\344\270\200\347\273\264\345\215\267\347\247\257\347\245\236\347\273\217\345\257\271\351\243\216\345\212\233\345\217\221\347\224\265\346\234\272\347\273\204\351\253\230\351\200\237\350\275\264\346\211\277\345\257\277\345\221\275\350\277\233\350\241\214\351\242\204\346\265\213.md" @@ -0,0 +1,399 @@ +--- +layout: post + +title: "使用一维卷积神经网络对风力发电机组高速轴承寿命进行预测" + +date: 2023-02-20 + +description: "主要包括使用一维卷积神经网络(1D CNN)对安装在远程风机的高速轴承中的现场传感器采集到的时间序列数据通过短时傅里叶变化之后得到的频谱峰度(Kurtosis Spectogram)进行分类,得到每个样品的类别:中等寿命级(预期寿命大于15天的轴承——状态良好)以及短寿命轴承(预期寿命小于15天的轴承——状态不良)。" + +tag: 机器学习 +--- +主要包括使用一维卷积神经网络(1D CNN)对安装在远程风机的高速轴承中的现场传感器采集到的时间序列数据通过短时傅里叶变化之后得到的频谱峰度(Kurtosis Spectogram)进行分类,得到每个样品的类别:中等寿命级(预期寿命大于15天的轴承——状态良好)以及短寿命轴承(预期寿命小于15天的轴承——状态不良)。 + +[原文链接:Wind Turbine High Speed Bearing Prognosis Kaggle](https://www.kaggle.com/code/luishpinto/wind-turbine-high-speed-bearing-prognosis) + +# 1. 问题描述 + +![轴承寿命预测](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230220141757.png) + +本文展示了使用振动监测传感器的原始数据来预测轴承的RUL(剩余使用寿命)的新方法,这种方法也可以用于其他类型的机器元件,如齿轮、联轴器等剩余寿命的预测。 + +本例中使用的数据是由安装在一个远程风力涡轮机的高速轴承上的现场传感器记录的。 +该轴承是齿轮箱的一部分,负责转子和发电机之间的耦合,特别是在高速轴上(发电机一侧)。 + +该数据可以通过以下方式访问: + +1. [WindTurbineHighSpeedBearingPrognosis-Data](https://github.com/mathworks/WindTurbineHighSpeedBearingPrognosis-Data) + +记录的变量是传感器在30天内测得的加速度(单位:g),每天6秒。 + +# 2. 技术背景 + +该方法是基于对50个样本中的每个样本的Kurtosis Spectogram(频谱峰度)的计算,并使用1D-CNN--一维卷积神经网络将每个样本划分为两个RUL等级。中等预期寿命类,代表轴承的预期寿命大于15天(状况良好)。 + +1. 中等预期寿命的类别:寿命大于15天(状况良好)。 +2. 短预期寿命类别,代表轴承的寿命期望值小于15天(状况不佳)。 + +基于这个假设,寿命最短的15个被标记为短寿命类,而所有剩余的被标记为中寿命类。 + +# 数据处理流程 + +## 导入包 + +```python +import numpy as np +import matplotlib.pyplot as plt + +import matplotlib +matplotlib.rcParams['mathtext.fontset'] = 'stix' +matplotlib.rcParams['font.family'] = 'sans-serif' +matplotlib.rcParams['font.size'] = 10 + +from scipy.io import loadmat # scipy加载mat文件 +from scipy.signal import stft # scipy的短时傅里叶变换 +from scipy.stats import kurtosis # kurtosis表示峰度,峰度是统计学中的一个概念,用来描述某个分布的峰值的陡峭程度 + +from mpl_toolkits.mplot3d import Axes3D + +from sklearn.metrics import accuracy_score, confusion_matrix, classification_report +from sklearn.model_selection import train_test_split + +from tensorflow.keras.models import * +from tensorflow.keras.layers import * +from tensorflow.keras.utils import * +``` + +## 定义混淆矩阵可视化函数 + +```python +def plotConfusionMatrix(dtrue,dpred,classes,\ + cmap = plt.cm.Blues,bsize = 1.0): # 混淆矩阵可视化函数,dtrue表示真实值,dpred表示预测值,classes表示类别,cmap表示颜色,bsize表示图像大小 + + cm = confusion_matrix(dtrue,dpred,normalize = 'true') # 混淆矩阵,normalize = 'true'表示归一化 + + fig,ax = plt.subplots(figsize = (np.shape(classes)[0] * 1.25 * bsize,\ + np.shape(classes)[0] * 1.25 * bsize)) + + im = ax.imshow(cm,interpolation = 'nearest',cmap = cmap) # interpolation = 'nearest'表示插值方式,nearest表示最近邻插值,cm表示混淆矩阵,cmap表示颜色 + + ax.set(xticks = np.arange(cm.shape[1]),\ + yticks = np.arange(cm.shape[0]),\ + xticklabels = classes,\ + yticklabels = classes,\ + ylabel = 'True Efficiency',\ + xlabel = 'Predicted Efficiency') + + plt.setp(ax.get_xticklabels(),rotation = 90,ha = 'right',\ + rotation_mode = 'anchor') # ha表示水平对齐方式,rotation_mode表示旋转模式,anchor表示锚点;setp表示设置属性 + + fmt = '.2f' # 保留两位小数 + + thresh = cm.max() / 2.0 + + for i in range(cm.shape[0]): + for j in range(cm.shape[1]): + ax.text(j,i,format(cm[i,j],fmt),ha = 'center',va = 'center',\ + color = 'white' if cm[i,j] > thresh else 'black') # ha表示水平对齐方式,va表示垂直对齐方式,j表示列,i表示行 + + fig.tight_layout() + + return ax +``` + +## 查看数据 + +扫描频率为97656HZ,采样的总共时长为6s,所以采样点的个数为 + +```python +fname = !ls './input/data' +fname = np.asarray(fname) + +df = [] + +for i in fname: + df.append(loadmat('./input/data/' + i)['vibration'].flatten())# loadmat表示加载mat文件,flatten表示降维 + +df = np.asarray(df) # 转换为数组 + +df.shape[0] + +``` + +输出结果为: + +```console +50 # 表示样本数量 +``` + +查看采样频率 + +```python +fs = int(df.shape[1] / 6.0) # 采样频率 +t = np.linspace(0.0,6.0,df.shape[1]) # df.shape[1]表示df的第二维度的长度,即采样点数;t表示一个周期内,每个采样点的时间; + +print('Scan frequency: {:,d} Hz'.format(fs)) # {:,d}表示以逗号分隔的十进制整数,scan frequency表示扫描频率 +``` + +输出结果为: + +```console +Scan frequency: 97,656 Hz +``` + +下图显示了加速度的测量值(单位:g),正如可以注意到的那样,加速度的振幅在时域上变化明显(x轴表示时间,每天6秒,50天) + +```python +plt.subplots(figsize = (12.0,6.0)) +for i in range(df.shape[0]): + plt.plot(t + i * 6.0,df[i],color = 'black',lw = 0.25) + +plt.xlabel('Time (in s): sample = 50 days in total, 6 seconds per day') +plt.ylabel('Acceleration (in g)') +plt.show() +``` + +![加速度振幅图](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230220145445.png) + +尽管加速度有明显的变化,记录的数据数量(6 x 97,656 = +每天585,936个记录;总共29,286,800个)很大,再加上 测量的非同步条件,使得使用传统的神经网络来对轴承状况进行分类是不可行的。 + +这个问题需要一个不同的方法来将正弦波的时域变量转化为非周期性变量,从而使区分RUL等级成为可能。 + +## Kurtosis + +这个新的变量是通过短时傅里叶变换计算的Kurtosis,如下图所示的三维图。可以注意到,在正常情况下,Kurtosis是非常稳定的,无论频率如何变化(例如前五个样本),但当异常的运行条件导致数据偏离正态分布时,则Kurtosis趋于变大。 + +```python +f = [] +fft = [] + +wnd = 127 + +for i in range(df.shape[0]): + u,v,w = stft(df[i],fs,nperseg = wnd,noverlap = int(0.8 * wnd),nfft = int(2.0 * wnd)) + # stft表示短时傅里叶变换,fs表示采样频率;nperseg表示每段的长度, + # noverlap表示重叠的长度,nfft表示fft的长度 + # u表示频率,v表示时域,w表示频谱 + f.append(u) + fft.append(kurtosis(np.abs(w),fisher = False,axis = 1)) # 对频谱取绝对值,然后计算峰度 + # fisher表示是否使用Fisher标准化,axis = 1表示沿着第二维度计算峰度 + +f = np.asarray(f) # asarray表示将输入转换为数组,f表示频率 +fft = np.asarray(fft) + +fftn = (fft - fft.min()) / (fft.max() - fft.min()) # 归一化,n表示归一化后的频谱 + +fftn = np.asarray(fftn) +n = np.ones_like(f[0])# ones_like表示返回与输入形状相同的数组,数组元素为1 +fig = plt.figure(figsize = (10.0,10.0)) +ax = plt.axes(projection = '3d') +ax.view_init(30,225) +for i in range(f.shape[0]): + ax.plot(f[i] / 1.0e3,i * n,fftn[i],color = 'black',linewidth = 0.50) # / 1.0e3表示将频率转换为kHz + +ax.set_xlabel('Frequency (kHz)') +ax.set_ylabel('Sample') +ax.set_zlabel('Kurtosis') +plt.show() +``` + +![频谱峰度图](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230220150240.png) + +## 模型训练 + +### 准备模型 + +Kurtosis被用作1D-CNN的输入(变量x),而与两个RUL等级相关的标签则通过变量y--中寿命期望值类(y = 0)用于前35个样本,而短寿命预期类(y = 1)用于最后15个样本。 +y被转换为分类格式,以适应神经网络的输出格式。 + +1D-CNN是通过使用Keras开源库的Sequential()函数定义的。 + +```python +X = fftn +X = X.reshape(-1,f.shape[1],1) # reshape表示改变数组的形状,-1表示自动计算,1表示一维,f.shape[1]表示频率的长度 +y = np.zeros(X.shape[0],dtype = int) +y[-15:] = 1 # y表示标签,-15表示从后往前数第15个,都是short-life expectancy class (y = 1) + +print('Input shape: {:d} samples x {:d} registers/sample'.format(X.shape[0],X.shape[1])) + +label = to_categorical(y) # to_categorical表示将标签转换为one-hot编码,one-hot编码表示将标签转换为二进制向量 +label +``` + +```console +Input shape: 50 samples x 128 registers/sample +``` + +```python +mdl = Sequential() # Sequential表示顺序模型 +mdl.add(Conv1D(100,6,activation = 'relu',\ + input_shape = (f.shape[1],1))) # Conv1D表示一维卷积层,100表示卷积核的个数,6表示卷积核的大小,activation表示激活函数 +mdl.add(Conv1D(100,6,activation = 'relu')) +mdl.add(MaxPooling1D(3)) +mdl.add(Conv1D(160,6,activation = 'relu')) +mdl.add(Conv1D(160,6,activation = 'relu')) +mdl.add(GlobalAveragePooling1D()) +mdl.add(Dropout(0.5)) +mdl.add(Dense(2,activation = 'softmax')) + +print(mdl.summary()) +``` + +输出结果如下: + +![模型总结](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230220150743.png) + +### 训练模型 + +```python +mdl.compile(loss = 'categorical_crossentropy',\ + optimizer = 'adam',metrics = ['accuracy']) # compile表示编译模型,loss表示损失函数,optimizer表示优化器,metrics表示评估模型的指标 +``` + +该模型在50%的样本上进行了训练--需要注意的是,该数据集非常有限,只有50个样本,尽管每个样本有大量的记录。 + +训练是基于1,000个epochs--这是为了让模型有一个良好的收敛。 + +代价矩阵(变量cw)被用来影响分类结果并避免与错误分类相关的错误。 + +当遇到很难决策的时候,**将中等寿命的轴承归类为短寿命轴承**,并将决定权交给维修专家,让维修专家来决定,而不是错误地将短寿命的轴承错误分类并导致机组故障。 + +```python +batch,epoch = 32,1000 # batch表示每次训练的样本数,epoch表示训练的轮数 +cw = {0: 5.0,1:75.0} # cw表示类别权重 + +Xtrain,Xtest,ytrain,ytest = train_test_split(X,label,\ + random_state = 21,\ + test_size = 0.50) +# train_test_split表示将数据集分为训练集和测试集,random_state表示随机数种子,test_size表示测试集的比例为50% + +hist = mdl.fit(Xtrain,ytrain,batch_size = batch,\ + epochs = epoch,validation_split = 0.2,verbose = 0,\ + class_weight = cw) +# fit表示训练模型,batch_size表示每次训练的样本数,epochs表示训练的轮数,validation_split表示验证集的比例,verbose表示日志显示,0表示不显示 +``` + +训练之后,画出accracy和loss随着训练次数的变化图。我们认为,接近1的正确率说明训练收敛情况良好。 + +```python +plt.subplots(1,2,figsize = (9.0,4.5),sharex = True) # sharex表示共享x轴 +plt.subplot(1,2,1) +plt.plot(hist.epoch,hist.history['accuracy'],\ + color = 'black',lw = 2.50) +# hist.epoch表示训练的轮数,hist.history['accuracy']表示训练集的准确率,可以看到随着训练的轮数增加,训练集的准确率也在增加 +plt.ylabel('1D-CNN Accuracy') +plt.xlabel('Epoch') +plt.subplot(1,2,2) +plt.plot(hist.epoch,hist.history['loss'],\ + color = 'pink',lw = 2.50) +plt.ylabel('1D-CNN Loss') +plt.xlabel('Epoch') +plt.tight_layout() +plt.show() +``` + +![accuracy and loss](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230220151615.png) + +可以发现,模型的准确率高到90%。 + +评估模型的变现: + +```python +print(mdl.evaluate(Xtest,ytest,verbose = 2)) # evaluate表示评估模型 +``` + +输出结果如下: + +```console +1/1 - 0s - loss: 0.4079 - accuracy: 0.9200 - 28ms/epoch - 28ms/step +[0.4079207181930542, 0.9200000166893005] +``` + +分类报告显示了每个类的精度、召回率和f1-score的详细信息,包括模型的整体性能。 + +```python +# print(classification_report(np.where(ytest != 0)[1],\ +# mdl.predict_classes(Xtest))) +predict=mdl.predict(Xtest) +classes_x=np.argmax(predict,axis=1) +print(classification_report(np.where(ytest != 0)[1],\ + classes_x)) +``` + +输出结果如下: + +```console +1/1 [==============================] - 0s 37ms/step + precision recall f1-score support + + 0 1.00 0.88 0.94 17 + 1 0.80 1.00 0.89 8 + + accuracy 0.92 25 + macro avg 0.90 0.94 0.91 25 +weighted avg 0.94 0.92 0.92 25 +``` + +通过下面描述的混淆矩阵,可以更直观地显示召回率: + +```python +plotConfusionMatrix(np.argmax(ytest,axis = 1),\ + np.argmax(mdl.predict(Xtest),axis = 1),\ + ['medium','short'],\ + bsize = 1.75,cmap = 'binary') +plt.title('Accuracy (RECALL)') +plt.show() +``` + +![召回率混淆矩阵](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230220152136.png) + +# 附录 + +## 深度学习评价标准 + +![Confusion matrix](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230220152541.png) + +TP(a):实际正类预测为正类的数量;FN(b):实际正类预测为负类的数量 + +FP(c):实际负类预测为正类的数量;TN(d):实际负类预测为负类的数量 + +IoU交并比:用于判断检测出的目标是否和真值目标框匹配,即判断是tp、fp还是fn + +$$ +IoU = tp/(tp+fp+fn) +$$ + +Precision 精确率(也就是查准率): + +$$ +Precision = tp /(tp+fp) +$$ + +Recall召回率(也即是查全率) + +$$ +Recall = tp/(tp+fn) +$$ + +P-R曲线 + +![P-R曲线](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230220153100.png) + +从图中可以看出,Precision高,Recall就很难做到非常高;Recall高,Precision就很难做到很高。因此,一般画出P-R曲线发现成反比例。于是,产生问题: + +1. 如何选取最合适的阈值以平衡Precision和Recall? 答案:可利用F1 score寻找Precision和Recall之间的最优平衡,即选择使得F1 score取得最大值时对应的阈值。 + +$$ +F1 = 2* (Precision * Recall)/(Precision+Recall) +$$ + +## 频谱峰度(Kurtosis Spectogram) + +谱峭度(Spectrum kurtosis,SK)的概念最先由Dwyer在1983年提出,其本质是计算每根谱线峭度值的高阶统计量。谱峭度对信号中的瞬态冲击成分十分敏感,能有效的从含有背景噪声信号中识别瞬态冲击及其在频带中的分布。由于谱峭度的复杂性、缺少一个正式的定义和一个容易理解的计算过程使其在很长时间内都未能引入到工程应用中,直到Antoni等在2006年为谱峭度做了详细的定义,并提出了基于短时傅里叶变换的SK估计器,将理论概念与实际应用联系起来,提出了Kurtogram算法,并阐述了该算法在旋转机械故障特征提取领域的具体应用过程。 + +[轴承故障诊断之谱峭度法的一些总结](https://zhuanlan.zhihu.com/p/266705380) + +## 1D CNN + +写不动了!!! diff --git "a/_posts/2023-02-21-Ubuntu\345\256\211\350\243\205 MySQL 8.0 \345\271\266\350\277\234\347\250\213\350\277\236\346\216\245.md" "b/_posts/2023-02-21-Ubuntu\345\256\211\350\243\205 MySQL 8.0 \345\271\266\350\277\234\347\250\213\350\277\236\346\216\245.md" new file mode 100644 index 0000000000..fcfe9ef90c --- /dev/null +++ "b/_posts/2023-02-21-Ubuntu\345\256\211\350\243\205 MySQL 8.0 \345\271\266\350\277\234\347\250\213\350\277\236\346\216\245.md" @@ -0,0 +1,227 @@ +--- +layout: post + +title: "Ubuntu安装 MySQL 8.0 并远程连接" + +date: 2023-02-21 + +description: "主要介绍在Ubuntu系统下面安装MySQL 8.0 并使用Navicat或者Vscode远程连接的具体步骤和坑" + +tag: Linux +--- +libpython3.7m.so.1.0: /usr/local/lib/libpython3.7m.so.1.0 + +# 安装 MySQL + +```bash +sudo apt update +sudo apt install mysql-server +``` + +# 查看默认用户名和密码 + +新版的MySQL安装后,默认用户名不是root,为了方便,一般我们需要修改成我们想要的用户名和密码。进入配置文件: + +```bash +vim /etc/mysql/debian.cnf +``` + +结果如下: + +![debian.cnf结果](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230224080255.png) + +默认的密码为: + +```console +user = debian-sys-maint +password = VDgQcUjzyfPfgxbA +``` + +# 登录数据库并修改用户名和密码 + +```bash +mysql -udebian-sys-maint -p +``` + +查看mysql中默认数据库:(记得sql结尾要加分号) + +```sql +show databases; +``` + +![show databases results](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230224080909.png) + +使用数据库mysql: + +```sql +use mysql; +``` + +![use mysql results](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230224081258.png) + +将root密码设置为空: + +```sql +update user set authentication_string='' where user='root'; +``` + +清空root用户默认的密码: + +```mysql +select user,authentication_string from user where user='root'; +``` + +![清空root用户默认的密码](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230224082514.png) + +为root设置密码:ALTER user + +```sql +ALTER user 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456'; +``` + +查看设置root用户和密码: + +```sql +select user,authentication_string from user where user='root'; +``` + +![查看设置root用户和密码](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230224083500.png) + +退出SQL: + +```sql +quit; +``` + +在mysql8.0之后的版本中,password函数已被取消,加密方式不再使用mysql_native_password,换成了caching_sha2_password. + +原本的sql语句为: + +```sql +UPDATE user SET authentication_string=password("密码") WHERE user="root"; +1 +``` + +对应的写法可以改为: + +```sql +UPDATE user SET authentication_string=SHA1("密码") WHERE user="root"; +``` + +# 配置MySQL远程登陆 + +```shell +#修改配置文件,注释掉 bind-address = 127.0.0.1 +$ sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf + +# 保存退出,然后进入mysql服务,执行授权命令:(输入密码123456) +$ mysql -uroot -p +``` + +![授权命令](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230224084410.png) + +localhost即本地主机的域名,如同:www.baidu.com ,也是本机ip地址,整个127.* 网段通常被用作loopback网络接口的默认地址,按照惯例通常设置为127.0.0.1。我们当前这个主机上的这个地址,别人不能访问,即使访问,也是访问自己。因为每一台TCP/IP协议栈的设备基本上都有local/127.0.0.1. + +进入数据库之后,查看当前主机配置信息为localhost,需要修改为%:(目的是为了让所有主机都能访问) + +```sql +use mysql; +select host from user where user='root'; +``` + +将Host设置为通配符%: + +```sql +update user set host = '%' where user ='root'; +``` + +修改后要刷新一下: + +```sql +flush privileges; +``` + +查看是否修改成功: + +```sql +select host from user where user='root'; +``` + +![已经修改成功](/_posts/image/2023-02-21-Ubuntu安装MySQL8.0并远程连接/1677199916004.png) + +退出数据库后重启数据库服务:(然后就可以在数据库工具远程MySQL数据库了。) + +```shell +sudo /etc/init.d/mysql restart #重启mysql服务 +``` + +# 在远程数据库远程连接MySQL数据库了 + +首先是进行测试,无论您如何安装它,MySQL 都应该自动开始运行。要对此进行测试,请检查其状态。 + +```shell +sudo systemctl status mysql +``` + +![MySQL已经自动运行](/_posts/image/2023-02-21-Ubuntu安装MySQL8.0并远程连接/1677200253300.png) + +然后,使用 ss 命令检查开放端口: + +```shell +sudo ss -ltn +``` + +![开放端口如下](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230224085823.png) + +可以看出,我们的服务器正在侦听端口 3306 和 33060 上的连接。这些是与 HTTP 和 MySQL 相关的端口。 + +在我们的本地系统上使用ss或者nmap localhost命令时,我们绕过了防火墙。实际上,这些命令显示处于侦听状态的端口,但这并不一定意味着端口对 Internet 开放,因为我们的防火墙可能拒绝连接。 + +使用以下命令检查 ufw 防火墙的状态。 + +```shell +sudo ufw status verbose +``` + +可以发现,防火墙状态为不活动。 + +![1677200490189](/_posts/image/2023-02-21-Ubuntu安装MySQL8.0并远程连接/1677200490189.png) + +如果防火墙状态为active状态, ufw 拒绝传入连接。由于端口 80 和 3306 尚未添加为例外,因此 HTTP 和 MySQL 无法接收传入连接,尽管ss并nmap报告它们处于侦听状态。 + +让我们使用以下命令为这些端口添加例外。 + +```shell +sudo ufw allow 80/tcp +sudo ufw allow 3306/tcp +``` + +[navicat安装破解教程](https://www.jianshu.com/p/9c4c499429da) + +使用ifconfig查看服务器ip地址,然后在navicat中连接数据库。 + +![连接数据库](/_posts/image/2023-02-21-Ubuntu安装MySQL8.0并远程连接/1677202388976.png) +连接结果如下: + +![连接结果](/_posts/image/2023-02-21-Ubuntu安装MySQL8.0并远程连接/1677202138195.png) + +也可以用VSCode来管理MySql数据库: + +1. 点击左侧扩展插件,然后搜索下MySql; +2. 安装过后,我们就需要连接了,直接快捷键,Ctrl+Shift+P,打开窗口; +3. 设置如下: + 1. ![设置如下](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230224093459.png) + 2. 端口号默认3306 + 3. 连接结果如下:![连接结果如下](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230224093706.png) + +# 参考链接 + +[Ubuntu安装MySQL8.0,并用Navicat远程连接_任求其-zzx的博客-CSDN博客](https://blog.csdn.net/qq_43685040/article/details/108732648) + +[Ubuntu 20.04 安装 MySQL 8.0 并且远程连接数据库(包括后续遇到的新坑)_Nymph2333的博客-CSDN博客_executing /lib/systemd/systemd-sysv-install enable](https://blog.csdn.net/u014378628/article/details/118406005) + +[在 Ubuntu 22.04 上安装 Python 3.9(多版本适用) - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/506491209) + +[Index of python-local/3.7.12 (huaweicloud.com)](https://mirrors.huaweicloud.com/python/3.7.12/) + +[在Ubuntu 18.04上安装不同版本的python及选择默认Python_WMLCOLIN的博客-CSDN博客](https://blog.csdn.net/weixin_42919435/article/details/109523985) diff --git "a/_posts/2023-02-24-python\347\211\271\346\200\247.md" "b/_posts/2023-02-24-python\347\211\271\346\200\247.md" new file mode 100644 index 0000000000..300057be46 --- /dev/null +++ "b/_posts/2023-02-24-python\347\211\271\346\200\247.md" @@ -0,0 +1,149 @@ +--- +layout: post + + +title: "Python面向对象编程之异常类" + + +date: 2023-02-24 + + +description: "主要包括定义自己的异常类" + + +tag: Python +--- + +# 定义自己的异常类 + +定义自己的错误类型有很多好处,比如可以清楚地显示出潜在的错误,让函数和模块更具可维护性。自定义错误类型还可用来提供额外的调试信息。 + +Python中比较常见的泛型异常类包括像 ValueError,但是在栈调试回溯中,返回的内容可能用处不大。 + +```python +class NameTooShortError(ValueError): + pass +def validate(name): + if len(name) < 10: + raise NameTooShortError(name) +``` + +引入自定义异常类型来表示名字验证失败,现在有了能够“顾名思义”NameTooShortError 异常类型,它扩展自内置的 ValueError类。(其他的内置Error还包括Exception和TypeError类) + +还可以使用继承来根据逻辑对异常分组,组成层次结构。 + +# 克隆对象 + +Python 中的赋值语句不会创建对象的副本,而只是将名称绑定到对象上。对于不可变对象也是如此。但为了处理可变对象或可变对象集合,需要一种方法来创建这些对象的“真实副本”或“克隆体”。 +有时需要用到对象的副本,以便修改副本时不会改动本体。 + +浅复制是指构建一个新的容器对象,然后填充原对象中子对象的引用。本质上浅复制只执行 +一层,复制过程不会递归,因此不会创建子对象的副本。 +深复制是递归复制,首先构造一个新的容器对象,然后递归地填充原始对象中子对象的副本。这种方式会遍历整个对象树,以此来创建原对象及其所有子项的完全独立的副本。 + +下面的例子中将创建一个新的嵌套列表,然后用 list()工厂函数浅复制: + +```python +xs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] +xs +ys = list(xs) +ys + +``` + +这里ys是一个新的独立对象,与xs具有相同的内容: + +```console +>>> xs +[[1, 2, 3], [4, 5, 6], [7, 8, 9]] +>>> ys +[[1, 2, 3], [4, 5, 6], [7, 8, 9]] +>>> +``` + +我们向原对象添加一个新列表,查看ys是否会跟着xs的变化而改变: + +```python-repl +xs.append(['new sublist']) +xs +ys +``` + +可以看到,修改浅复制的列表完全不会影响副本。 + +```console +>>> xs +[[1, 2, 3], [4, 5, 6], [7, 8, 9], ['new sublist']] +>>> ys +[[1, 2, 3], [4, 5, 6], [7, 8, 9]] +>>> +``` + +但由于前面只创建了原列表的浅副本,所以 ys 仍然含有 xs 子对象的引用! + +这些子对象没有复制,只是在 ys 中再次引用。因此在修改 xs 中的子对象时,这些改动也会反映在 ys 中——因为两个列表共享相同的子对象。这个副本是仅含有一层的浅复制: + +```python +xs[1][0] = 'X' +xs +ys + +``` + +这里看上去这是修改了xs,但是ys和xs中索引1处的子列表都被修改了。 + +```console +>>> xs +[[1, 2, 3], ['X', 5, 6], [7, 8, 9], ['new sublist']] +>>> ys +[[1, 2, 3], ['X', 5, 6], [7, 8, 9]] +>>> +``` + +而如果在第一步中创建的是 xs 的深副本,那么这两个对象会互相完全独立。 + + +## 制作深副本 + +使用 copy 模块中定义的 deepcopy()函数创建深副本: + +```python +import copy +xs = [[1,2,3],[4,5,6],[7,8,9]] +zs = copy.deepcopy(xs) +``` + +这里,它们看起来是一样的。 + +```console +>>> xs +[[1, 2, 3], [4, 5, 6], [7, 8, 9]] +>>> zs +[[1, 2, 3], [4, 5, 6], [7, 8, 9]] +>>> +``` + +我们看到如果修改原对象(xs)中的某个子对象,则会发现这些修改不会影响深副本(zs)。 + +```python +xs[1][0] = 'X' +xs +zs +``` + +输出结果如下: + + +```python +>>> xs +[[1, 2, 3], ['X', 5, 6], [7, 8, 9]] +>>> zs +[[1, 2, 3], [4, 5, 6], [7, 8, 9]] +>>> +``` + +还可以使用 copy 模块中的一个函数来创建浅副本。copy.copy()函数会创建 +对象的浅副本。 + +在代码中,copy.copy()可以清楚地表明这里创建的是浅副本。但对于**内置容器**,只需要 +使用 list、dict 和 set 这样的工厂函数就能创建浅副本,这种方式更具 Python 特色. diff --git "a/_posts/2023-03-07-Linux\345\242\236\345\212\240\345\256\232\346\227\266\350\260\203\345\272\246\344\273\273\345\212\241\345\271\266\346\257\217\346\227\245\344\277\235\345\255\230syslog\346\226\207\344\273\266\345\210\260\346\214\207\345\256\232\347\233\256\345\275\225.md" "b/_posts/2023-03-07-Linux\345\242\236\345\212\240\345\256\232\346\227\266\350\260\203\345\272\246\344\273\273\345\212\241\345\271\266\346\257\217\346\227\245\344\277\235\345\255\230syslog\346\226\207\344\273\266\345\210\260\346\214\207\345\256\232\347\233\256\345\275\225.md" new file mode 100644 index 0000000000..6a17cc6be1 --- /dev/null +++ "b/_posts/2023-03-07-Linux\345\242\236\345\212\240\345\256\232\346\227\266\350\260\203\345\272\246\344\273\273\345\212\241\345\271\266\346\257\217\346\227\245\344\277\235\345\255\230syslog\346\226\207\344\273\266\345\210\260\346\214\207\345\256\232\347\233\256\345\275\225.md" @@ -0,0 +1,80 @@ +--- +layout: post +title: "Linux增加定时调度任务并每日保存syslog文件到指定目录" +date: 2023-03-07 +description: "介绍Linux增加定时调度任务并且使用SHELL脚本编程,将日志文件syslog备份到指定目录" +tag: Linux +--- +# 日志文件备份 + +有时候,为了系统稳定性,除了在/var/log通过回滚机制生成各种日志文件以排查系统故障之外,我们也想把这些关键的日志文件保存到指定文件夹,防止突然断电或者磁盘分区被破坏之后丢失关键的日志信息。 + +代码如下:(文件名为backup_sysconfig.sh) + +```shell +#! /bin/bash +# this script is used to backup the syslog file in /var/log dir everyday +echo `date` +cp /var/log/syslog /home/hugo/test_BASH/log +# adding the user with x permission +chmod u+x syslog +# chnage the firname +today=$(date +%y%m%d) +echo $today +mv ./log/syslog ./log/syslog_$today # 同一个文件夹下面就相当于重命名 +``` + +## 定时调度任务 +crontab 的语句是“分 时 日 月 周 命令”. + +开启定时调度任务,每天凌晨1点执行一次 + +```shell +crontab -e +# 0 1 * * * /home/hugo/test_BASH/back_sysconfig.sh +``` +显示`crontab: installing new crontab`,表示定时任务已经开启。 + +查看定时任务 + +```shell +crontab -l +``` + +开启或者关闭定时任务 + +```shell +service crond start +service crond stop +``` + +发现报错:`Failed to start crond.service: Unit crond.service not found.` + +删除调度任务: + +* crontab -r ,删除所有任务调度工作 +* 直接编辑 vim /etc/crontab ,文件内容如下: + +```console +# Unlike any other crontab you don't have to run the `crontab' +# command to install the new version when you edit this file +# and files in /etc/cron.d. These files also have username fields, +# that none of the other crontabs do. + +SHELL=/bin/sh +# You can also override PATH, but by default, newer versions inherit it from the environment +#PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + +# Example of job definition: +# .---------------- minute (0 - 59) +# | .------------- hour (0 - 23) +# | | .---------- day of month (1 - 31) +# | | | .------- month (1 - 12) OR jan,feb,mar,apr ... +# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat +# | | | | | +# * * * * * user-name command to be executed +17 * * * * root cd / && run-parts --report /etc/cron.hourly +25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily ) +47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly ) +52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly ) +``` diff --git "a/_posts/2023-03-16-ubuntu\344\270\213\344\277\256\346\224\271apt\346\272\220\351\227\256\351\242\230(\346\227\240\346\263\225\350\247\243\346\236\220\345\237\237\345\220\215).md" "b/_posts/2023-03-16-ubuntu\344\270\213\344\277\256\346\224\271apt\346\272\220\351\227\256\351\242\230(\346\227\240\346\263\225\350\247\243\346\236\220\345\237\237\345\220\215).md" new file mode 100644 index 0000000000..8e6c72e29f --- /dev/null +++ "b/_posts/2023-03-16-ubuntu\344\270\213\344\277\256\346\224\271apt\346\272\220\351\227\256\351\242\230(\346\227\240\346\263\225\350\247\243\346\236\220\345\237\237\345\220\215).md" @@ -0,0 +1,44 @@ +--- +layout: post +title: "ubuntu下修改apt源问题(无法解析域名)" +date: 2023-03-16 +description: "介绍Linux的ubuntu下修改apt源问题(无法解析域名)" +tag: Linux +--- +apt修改为阿里源, `sudo vim /etc/apt/sources.list`之后出现问题: + + +```console +W: 无法下载 http://mirrors.aliyun.com/ubuntu/dists/jammy/InRelease 暂时不能解析域名“mirrors.aliyun.com” +W: 无法下载 http://mirrors.aliyun.com/ubuntu/dists/jammy-security/InRelease 暂时不能解析域名“mirrors.aliyun.com” +W: 无法下载 http://mirrors.aliyun.com/ubuntu/dists/jammy-updates/InRelease 暂时不能解析域名“mirrors.aliyun.com” +W: 无法下载 http://mirrors.aliyun.com/ubuntu/dists/jammy-proposed/InRelease 暂时不能解析域名“mirrors.aliyun.com” +W: 无法下载 http://mirrors.aliyun.com/ubuntu/dists/jammy-backports/InRelease 暂时不能解析域名“mirrors.aliyun.com” +W: 无法下载 https://ppa.launchpadcontent.net/deadsnakes/ppa/ubuntu/dists/jammy/InRelease 暂时不能解析域名“ppa.launchpadcontent.net” +W: 部分索引文件下载失败。如果忽略它们,那将转而使用旧的索引文件。 + +``` + + +## 原因 + +DNS没有配置好 + +## 解决 + +```sh +sudo vim /etc/resolv.conf +# 或 +sudo vim /run/systemd/resolve/stub-resolv.conf # 建议改这个 +``` + +添加nameserver,如下:(使用阿里巴巴提供的DNS域名解析服务) + +```sh +# 添加 +# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8) +# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN + +nameserver 223.5.5.5 +nameserver 223.6.6.6 +``` diff --git "a/_posts/2023-03-16-\345\210\244\346\226\255\346\230\257\345\220\246\346\227\266\347\264\240\346\225\260\345\207\275\346\225\260\347\232\204\345\215\225\345\205\203\346\265\213\350\257\225.md" "b/_posts/2023-03-16-\345\210\244\346\226\255\346\230\257\345\220\246\346\227\266\347\264\240\346\225\260\345\207\275\346\225\260\347\232\204\345\215\225\345\205\203\346\265\213\350\257\225.md" new file mode 100644 index 0000000000..a79dfa9122 --- /dev/null +++ "b/_posts/2023-03-16-\345\210\244\346\226\255\346\230\257\345\220\246\346\227\266\347\264\240\346\225\260\345\207\275\346\225\260\347\232\204\345\215\225\345\205\203\346\265\213\350\257\225.md" @@ -0,0 +1,192 @@ +--- +layout: post + +title: "判断是否时素数函数的单元测试" + +date: 2023-03-16 + +description: "利用pytest自动测试框架构建判断是否时素数函数的单元测试" + +tag: Python +--- + + +## 单元测试 + +单元测试的英文是Unit Testing,特指在一个分隔的代码单元中进行的测试,一个单元可以是整个模块,一个单独的类或者函数。 + +编写单元测试的代码可以帮助我们在早期快速找到bug,以较小的代价在早期找到bug。 + +## 被测函数 + +我们先编写一个函数,判断输入数据是否是素数,并且可以判断距离这个数最近的素数。 + + +```python +. +# Author: chenjunllin +# Date: 2022-03-16 +# File: is_prime.py + + +def is_prime(number): + """如果是质数,返回为True""" + for element in range(2, number): + if number % element == 0: + return False + return True + + +def print_next_prime(number): + """打印输入number数字最近的质数""" + index = number + while True: + index += 1 + if is_prime(index): + print(index) +``` + +## 测试is_prime函数 + +使用测试驱动开发(TDD)的方法,编写的最简单的测试代码测试is_prime函数是否实现了功能,代码如下: + +```python + +# Author: chenjunlin +# Date: 2023-03-16 +# File: test_is_prime.py + +import pytest + +from src.chapter7unit.method_examples.is_prime import is_prime + + +class TestPrimes(object): + """测试is_prime""" + + def test_is_zero_not_prime(self): + """测试边界值0是不是质数""" + assert is_prime(0) is False + + def test_is_one_not_prime(self): + """测试边界值1是不是质数""" + assert is_prime(1) is False + + def test_is_two_prime(self): + """2是质数""" + assert is_prime(2) is True + + def test_is_four_non_prime(self): + """4不是质数""" + assert is_prime(4) is False + + def test_is_five_prime(self): + """5是不是质数?""" + assert is_prime(5) is True + + +if __name__ == '__main__': + pytest.main() + +``` + +输出结果如下: + +也就是说,0和1不是素数,但是函数返回的结果是1,说明is_prime函数没有实现功能。 +![20230316105600](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230316105600.png) + +回到原来的函数,发现是因为range函数的范围是从2到number,所以0和1都没有被判断,所以我们修改一下代码,如下: + +```python +def is_prime(number): + """如果是质数,返回为True""" + if number < 2: + return False + for element in range(2, number): + if number % element == 0: + return False + return True +``` + +再次运行test_is_prime.py测试代码,发现测试通过了。 + +![20230316105849](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230316105849.png) + + +## 测试print_next_prime函数 + +我们再来编写一个测试函数,测试print_next_prime函数,代码如下: + +```python +# Author: chenjunlin +# Date: 2023-03-16 +# File: test_print_is_prime.py + +import pytest + +from src.chapter7unit.method_examples.is_prime import print_next_prime + +class TestPrimes(object): + """测试print_next_prime""" + + def test_print_next_prime_0(self): + """ 0的最近质数是2 """ + assert print_next_prime(0) is 2 + + def test_print_next_prime_4(self): + """ 4的最近质数是5 """ + assert print_next_prime(4) is 5 + + def test_print_next_prime_5(self): + """ 5的最近质数是7 """ + assert print_next_prime(5) is 7 + + def test_print_next_prime_7(self): + """ 7的最近质数是11 """ + assert print_next_prime(7) is 11 + + +if __name__ == '__main__': + pytest.main() +``` + +没有通过测试,结果如下: + +![1678936036418](/_posts/image/2023-03-16-判断是否时素数函数的单元测试/1678936036418.png) + + +原来是因为print_next_prime函数返回结果并不是距离输入数字最近的素数,而是返回了所有大于输入数字的素数,所以在running tests中,我们可以发现一直在打转,没有返回assert断言的结果。 + +我们还可以通过在is_prime.py中添加魔术方法__name__ == '__main__'来测试print_next_prime函数,代码如下: + +```python +if __name__ == '__main__': + + print(is_prime(2)) + print_next_prime(0) + +``` + +发现结果确实是打印了所有大于输入数字的素数: + +![1678936579647](/_posts/image/2023-03-16-判断是否时素数函数的单元测试/1678936579647.png) + + +我们修改一下代码,如下: + +```python +def print_next_prime2(number): + """打印输入number数字最近的质数""" + index = number + while True: + index += 1 + if is_prime(index): + return index +``` + +再次运行测试代码,发现测试通过了。 + + + + + diff --git "a/_posts/2023-03-17-\344\275\277\347\224\250chatGPT\345\255\246\344\271\240BDD\346\216\245\345\217\243\346\265\213\350\257\225.md" "b/_posts/2023-03-17-\344\275\277\347\224\250chatGPT\345\255\246\344\271\240BDD\346\216\245\345\217\243\346\265\213\350\257\225.md" new file mode 100644 index 0000000000..076ca96cde --- /dev/null +++ "b/_posts/2023-03-17-\344\275\277\347\224\250chatGPT\345\255\246\344\271\240BDD\346\216\245\345\217\243\346\265\213\350\257\225.md" @@ -0,0 +1,250 @@ +--- +layout: post +title: "使用chatGPT学习BDD接口测试" +date: 2023-03-17 +description: "介绍了基于chatGPT学习行为驱动开发接口自动化测试" +tag: 自动化测试 +--- + +## BDD接口测试是什么? + +BDD (Behavior Driven Development) 是一种软件开发方法,旨在通过定义和执行行为来增强软件的可理解性和可维护性。在接口测试中,BDD 可以帮助我们更好地描述接口的行为和期望结果,从而提高测试用例的可读性和可维护性。 + +Pytest-BDD 是一个 Pytest 插件,它提供了对 BDD 测试的支持。下面是一些使用 Pytest-BDD 进行接口测试的步骤: + +1. 安装 Pytest-BDD + +可以使用 pip 命令安装 Pytest-BDD: + +``` +pip install pytest-bdd +``` + +2. 定义 Feature 文件 + +在 BDD 中,测试用例通常以 Feature 文件的形式进行组织。Feature 文件是一个文本文件,用于描述测试用例的场景和期望结果。例如,以下是一个示例 Feature 文件: + +```gherkin +Feature: User API + +Scenario: Create a new user + Given I have a valid access token + When I make a POST request to "/users" with the following data + | username | john_doe | + | email | john_doe@example.com | + | password | 123456 | + Then the response status code should be 201 + And the response should contain the following data + | username | john_doe | + | email | john_doe@example.com | +``` + +在这个示例中,Feature 文件定义了一个名为 "User API" 的 Feature,其中包含一个名为 "Create a new user" 的 Scenario。Scenario 中描述了测试用例的场景和期望结果,包括给定的前提条件 (Given)、操作 (When) 和断言 (Then)。 + +3. 定义 Step Definitions + +Step Definitions 是一个 [Python]() 模块,用于实现 Feature 文件中定义的 Given、When 和 Then 步骤。例如,以下是一个示例 Step Definitions: + +```python +import requests +from pytest_bdd import given, when, then, parsers + +@given("I have a valid access token") +def valid_access_token(): + # 返回一个有效的访问令牌 + return "valid_token" + +@when(parsers.parse('I make a {method} request to "{url}" with the following data')) +def make_request(method, url): + # 根据方法和 URL 发送请求 + # 将数据作为请求正文发送 + response = requests.request(method, url, json=parsers.parse_table()) + return response + +@then(parsers.parse('the response status code should be {status_code:d}')) +def check_status_code(make_request, status_code): + assert make_request.status_code == status_code + +@then(parsers.parse('the response should contain the following data')) +def check_response_data(make_request): + expected_data = parsers.parse_table() + actual_data = make_request.json() + assert expected_data in actual_data.values() +``` + +在这个示例中,Step Definitions 实现了 Feature 文件中定义的 Given、When 和 Then 步骤。[Step Definitions]() 使用 Pytest-BDD 提供的装饰器 (如 @given、@when 和 @then) 来标记步骤,以及 Pytest-BDD 提供的 parsers.parse 方法来解析表格数据。 + +4. 运行测试用例 + +使用 Pytest 运行测试用例时,可以使用 `--bdd` 参数来指定测试目录和模式,如: + +``` +pytest --bdd tests/ --verbose +``` + +在运行测试用例时,Pytest-BDD 将自动扫描测试目录下的 Feature 文件和 Step Definitions,然后根据 Feature 文件定义的场景执行测试用例。 + +总之,使用 Pytest-BDD 进行接口测试可以帮助我们更好地描述接口的行为和期望结果,从而提高测试用例的可读性和可维护性。建议了解 Pytest-BDD 官方文档中有关 BDD 测试的更多内容,以便更好地使用 Pytest-BDD 进行接口测试。 + +## 定义 Feature 文件的格式可以展开说明一下吗 + +Feature 文件是 BDD 测试中用于描述测试用例的场景和期望结果的文本文件。它通常采用 Gherkin 语言进行编写,Gherkin 语言是一种基于自然语言的 [DSL]() (Domain Specific Language),它提供了一种简单易懂的语法,用于描述测试用例的场景和期望结果。 + +下面是一个 Feature 文件的示例,它描述了一个用户管理系统的测试用例场景: + +```gherkin +Feature: User Management + As a system administrator + I want to manage users + So that I can manage access to the system + + Scenario: Create a new user + Given I am logged in as an administrator + When I create a new user with the following details + | username | john_doe | + | email | john_doe@example.com | + | password | 123456 | + Then the user should be created successfully + + Scenario: Update an existing user + Given I am logged in as an administrator + And there is an existing user with the username "jane_doe" + When I update the user with the username "jane_doe" with the following details + | email | jane_doe@example.com | + | password | 654321 | + Then the user should be updated successfully + + Scenario: Delete an existing user + Given I am logged in as an administrator + And there is an existing user with the username "jane_doe" + When I delete the user with the username "jane_doe" + Then the user should be deleted successfully +``` + +在这个示例中,Feature 文件包含了一个名为 "User Management" 的 Feature,它描述了一个用户管理系统的测试用例场景。Feature 文件中的每个 Scenario 描述了一个测试用例,包括前提条件 (Given)、操作 (When) 和断言 (Then)。 + +Feature 文件的基本语法格式如下: + +``` +Feature: + + + Scenario: + Given + When + Then + + Scenario: + Given + When + Then + + ... +``` + +其中,Feature、Scenario、Given、When 和 Then 等关键字都是 Gherkin 语言中的保留关键字,用于描述测试用例的场景和期望结果。Feature 文件的详细格式和语法可以参考 Gherkin 语言的官方文档和 Pytest-BDD 的官方文档。 + + +## Step Definitions中的@when(parsers.parse('I make a request to "" with the following data'))我看不太懂 + +这段 Step Definitions 中的 `@when(parsers.parse('I make a request to "{url}" with the following data'))` 是一个装饰器,用于标记一个 When 步骤。该步骤对应 Feature 文件中的一行,例如: + +```gherkin +When I make a request to "https://example.com/api/users" with the following data +``` + +装饰器的参数 `parsers.parse('I make a request to "{url}" with the following data')` 表示使用 `parsers.parse()` 方法来解析 Feature 文件中的文本,并将其中的 `{url}` 参数解析为 Step Definitions 函数的 `url` 参数。 + +当 Pytest-BDD 执行测试用例时,它会将 Feature 文件中的文本传递给 Step Definitions 函数,例如: + +```python +@when(parsers.parse('I make a request to "{url}" with the following data')) +def make_request(url): + # 执行 HTTP 请求,使用 url 和数据表格中的数据 + pass +``` + +在这个示例中,`make_request()` 函数接受一个 `url` 参数,并且使用该参数和数据表格中的数据来执行 HTTP 请求。例如,如果 Feature 文件中的文本是 `When I make a request to "https://example.com/api/users" with the following data`,则 Pytest-BDD 会将 `https://example.com/api/users` 作为 `url` 参数传递给 `make_request()` 函数,并且将数据表格作为函数的参数传递。 + +希望这样的解释能够帮助您理解 Step Definitions 中的装饰器和参数。 + + +## 我看不太懂@then(parsers.parse('the response status code should be')) def check_status_code(make_request, status_code): assert make_request.status_code == status_code + +这段代码是一个 [Step Definitions]() 中的 `@then` 装饰器,用于标记一个 Then 步骤。该步骤对应于 Feature 文件中的一行,例如: + +```gherkin +Then the response status code should be 200 +``` + +装饰器的参数 `parsers.parse('the response status code should be {status_code:d}')` 表示使用 `parsers.parse()` 方法来解析 Feature 文件中的文本,并将其中的 `{status_code}` 参数解析为 Step Definitions 函数的 `status_code` 参数。`d` 表示将该参数解析为整数类型。 + +当 Pytest-BDD 执行测试用例时,它会将 Feature 文件中的文本传递给 Step Definitions 函数,例如: + +```python +@then(parsers.parse('the response status code should be {status_code:d}')) +def check_status_code(make_request, status_code): + assert make_request.status_code == status_code +``` + +在这个示例中,`check_status_code()` 函数接受一个 `make_request` 参数和一个 `status_code` 参数。`make_request` 参数是一个在之前的 Given 或 When 步骤中执行的请求对象(例如使用 requests 库发出的 HTTP 请求),而 `status_code` 参数则是从 Feature 文件中解析出来的 HTTP 状态码。 + +该函数的实现是检查 `make_request` 对象的状态码是否等于 `status_code` 参数。如果状态码不相等,那么该断言会失败,从而标记该测试用例为失败。 + +希望这样的解释能够帮助您理解 Step Definitions 中的装饰器和参数,并理解其在 Pytest-BDD 中的作用。 + + +## BDD接口自动化测试怎么管理很多的测试数据呢 + + +在 BDD 接口自动化测试中,管理大量测试数据的方法有很多种,具体根据项目的实际情况和需求来选择。 + +以下是一些常用的管理测试数据的方法: + +1. 数据库管理:将测试数据存储在数据库中,并使用 SQL 语句来获取和操作数据。这种方法可以方便地进行数据的增删改查和管理,但需要一些数据库知识和维护成本。 +2. Excel/CSV 文件管理:将测试数据存储在 [Excel]() 或 [CSV]() 文件中,使用 [Python]() 的 pandas 库或其他数据处理库来读取和操作数据。这种方法可以方便地进行数据的导入导出和管理,但需要一些额外的开发工作。 +3. 配置文件管理:将测试数据存储在配置文件中,例如 [YAML]() 或 [JSON]() 格式的文件,使用 Python 的 [ConfigParser]() 或其他配置文件解析库来读取和操作数据。这种方法可以方便地进行数据的配置和管理,但需要一些额外的开发工作。 +4. 数据生成工具:使用 Python 的 Faker 库或其他数据生成库来生成测试数据。这种方法可以方便地生成大量的测试数据,但需要确保生成的数据符合业务逻辑和要求。 + +无论采用哪种方法,都需要注意测试数据的可维护性和可重用性,以便在测试用例变更或新增时能够快速地修改或添加测试数据。同时,也需要对测试数据进行合理的组织和分类,以便于测试用例的管理和维护。 + +## 可以更加具体说一下这个步骤吗,还是不懂怎么写入大数据到feature中 + + +如果您需要在 Feature 文件中写入大量的测试数据,可以使用数据表格来组织数据,并将数据表格写入 Feature 文件中。数据表格是 Feature 文件中以表格格式表示的数据,可以包含多行和多列的数据。 + +以下是一个示例 Feature 文件,其中包含一个数据表格: + +```gherkin +Feature: 订单管理 + + Scenario: 创建订单 + Given 用户登录系统 + When 用户创建订单 + | 商品名称 | 数量 | + | 商品A | 2 | + | 商品B | 1 | + Then 订单创建成功 +``` + +在上面的示例中,数据表格位于 `When` 步骤下面,使用管道符 `|` 来分隔每一列的数据。第一行表示每一列的列名,后面的行表示实际的数据。您可以根据需要添加或删除列和行,以适应不同的测试场景。 + +[在 Step Definitions]() 中,您可以使用 Pytest-BDD 提供的 `parsers.parse_table()` 方法来解析数据表格,并将其转换为 Python 中的数据结构,例如列表或字典。以下是一个示例 [Step Definitions](): + +```python +@when(parsers.parse('用户创建订单')) +def create_order(): + pass + +@when(parsers.parse('用户创建订单')) +def create_order(make_request, data): + for row in data: + product_name = row['商品名称'] + quantity = row['数量'] + # 使用商品名称和数量创建订单项 + pass +``` + +在上面的示例中,`create_order()` 函数接受一个 `make_request` 参数和一个 `data` 参数,其中 `data` 参数是通过 `parsers.parse_table()` 方法解析出来的数据表格。在函数中,我们可以遍历数据表格的每一行,并使用其中的数据来执行一系列操作,例如创建订单项。 + +通过使用数据表格和 `parsers.parse_table()` 方法,您可以方便地将大量测试数据写入 Feature 文件和 Step Definitions 中,并且可以轻松地扩展和维护这些数据。 diff --git "a/_posts/2023-03-17-\344\275\277\347\224\250chatGPT\345\255\246\344\271\240\346\234\272\345\231\250\345\255\246\344\271\240\347\256\227\346\263\225.md" "b/_posts/2023-03-17-\344\275\277\347\224\250chatGPT\345\255\246\344\271\240\346\234\272\345\231\250\345\255\246\344\271\240\347\256\227\346\263\225.md" new file mode 100644 index 0000000000..a28ffe1a82 --- /dev/null +++ "b/_posts/2023-03-17-\344\275\277\347\224\250chatGPT\345\255\246\344\271\240\346\234\272\345\231\250\345\255\246\344\271\240\347\256\227\346\263\225.md" @@ -0,0 +1,611 @@ +--- +layout: post +title: "使用chatGPT学习机器学习算法" +date: 2023-03-17 +description: "介绍了基于chatGPT学习机器学习算法并应用到风机健康监控中等等" +tag: 机器学习 +--- + +## wind turbine数据,如何使用机器学习算法预测轴承健康程度和风功率曲线呢 + +可以使用机器学习算法进行预测。以下是一个基本的流程,希望能为您提供一些帮助: + +1. 数据收集和准备:收集风力涡轮机的传感器数据,包括机械振动、电流、温度等。确保数据集中包含健康和故障状态下的数据,并对数据进行清理和处理,例如去除缺失值和异常值。 +2. 特征工程:从原始数据中提取有用的特征,例如频率和幅度特征。可以使用信号处理技术、统计学方法或深度学习方法进行特征提取。 +3. 模型选择和训练:选择适当的机器学习算法进行轴承健康程度和风功率预测,例如支持向量机(SVM)、随机森林(Random Forest)、神经网络等。使用训练数据对模型进行训练,并使用交叉验证技术评估模型性能。 +4. 模型优化和部署:通过调整模型参数和优化特征工程过程,进一步提高模型性能。使用测试数据集评估模型的性能,并将其部署到实际应用中。 + +需要注意的是,风能发电量预测的准确性受多种因素影响,包括风速、风向、空气密度、温度等。因此,最好使用多变量模型进行预测,并考虑使用其他数据源(如天气数据)来提高预测准确性。 + +## 第一步需要收集什么样的数据,怎么进行数据预处理呢 + +在收集风力涡轮机数据时,需要收集与轴承健康状态和风能发电量相关的传感器数据,例如振动、电流、温度等。这些传感器数据可以通过直接连接到风力涡轮机上的传感器进行收集,也可以通过数据日志记录器进行收集。 + +对于轴承健康状态的预测,需要收集不同状态下的传感器数据,例如正常运行时和轴承故障时的振动数据。收集的数据应该包含不同的轴承类型、负载条件和环境条件。 + +对于风能发电量的预测,需要收集与风能发电量相关的传感器数据,例如风速、风向、温度、气压等。这些数据可以来自风场的气象站,也可以通过安装在风力涡轮机上的传感器进行收集。 + +在进行数据预处理时,需要进行以下步骤: + +1. 数据清洗:去除缺失值、异常值、重复值等。 +2. 数据转换:将数据转换为适合机器学习算法处理的形式,例如将时间序列数据转换为频域数据。 +3. 特征提取:从原始数据中提取有用的特征,例如频率和幅度特征。 +4. 特征缩放:将特征缩放到相同的范围内,以便机器学习算法更好地处理数据。 +5. 特征选择:选择与轴承健康状态和风能发电量预测相关的特征。 +6. 数据划分:将数据集划分为训练集、验证集和测试集。 + +以上步骤可以提高机器学习算法的性能,并帮助提高预测准确性。 + +## 第一步数据清理python代码 + +以下是一些Python代码示例,用于数据清理: + +1. 去除缺失值 + +```python + +import pandas as pd + + +# 读取数据集 + +df = pd.read_csv('data.csv') + + +# 去除缺失值 + +df.dropna(inplace=True) + +``` + +2. 去除异常值 + +```python + +import numpy as np + +import pandas as pd + + +# 读取数据集 + +df = pd.read_csv('data.csv') + + +# 计算异常值的阈值 + +mean = np.mean(df['column_name']) + +std = np.std(df['column_name']) + +threshold = 3 * std + + +# 去除异常值 + +df = df[np.abs(df['column_name'] - mean) < threshold] + +``` + +3. 去除重复值 + +```python + +import pandas as pd + + +# 读取数据集 + +df = pd.read_csv('data.csv') + + +# 去除重复值 + +df.drop_duplicates(inplace=True) + +``` + +这些代码示例仅仅是对数据清洗的基本操作,实际情况需要根据数据集的具体情况进行适当的修改。 + +## 重复的取值需要去掉吗 + +一般来说,重复的取值需要去掉。重复的取值可能会对数据分析和建模产生影响,因此在进行数据清洗时,通常会去除重复的取值。 + +在Python中,可以使用Pandas库的drop_duplicates()方法来去除DataFrame中的重复行。例如,假设我们有一个名为df的DataFrame,其中包含重复的行,可以按如下方式去除重复的行: + +```python + +import pandas as pd + + +# 读取数据集 + +df = pd.read_csv('data.csv') + + +# 去除重复行 + +df.drop_duplicates(inplace=True) + +``` + +需要注意的是,drop_duplicates()方法默认根据所有列的值来判断是否为重复行,如果只想根据特定列的值来判断是否为重复行,可以在方法中指定subset参数,例如: + +```python + +# 根据 'column_name' 列的值来去除重复行 + +df.drop_duplicates(subset=['column_name'], inplace=True) + +``` + +另外,有些情况下,重复的取值可能是由于数据采集或存储过程产生的,如果确定这些重复的取值不会对数据分析和建模产生影响,可以保留这些重复的取值。 + +## 请问不同的数据之间需要归一化吗,怎么确定在建模过程中不同数据的权重呢 + +在建模过程中,不同的数据通常需要进行归一化。归一化的目的是将不同数据之间的尺度统一,避免不同数据之间的尺度差异对建模结果产生影响。例如,在使用基于距离的算法(如KNN)时,如果不对数据进行归一化,那么具有较大尺度的数据特征将会对距离计算产生更大的影响,从而可能会导致错误的分类结果。 + +常用的归一化方法包括最小-最大归一化和Z-score归一化。最小-最大归一化将数据缩放到[0,1]的范围内,而Z-score归一化将数据转换为均值为0,标准差为1的分布。在使用这些方法进行归一化时,需要注意对训练集和测试集进行相同的归一化处理,以避免在测试集上产生不合理的结果。 + +在建模过程中,不同数据的权重通常是通过特征选择、特征提取或特征工程等方法来确定的。这些方法可以根据不同的场景和问题,选择合适的特征,从而确定不同数据的权重。在选择特征时,需要考虑特征的相关性、重要性和稳定性等因素,并尽可能地保留有用的信息,以提高建模的准确性和泛化性能。 + +## 我对这句话理解不够深入“不同数据的权重通常是通过特征选择、特征提取或特征工程等方法来确定的”,请问有具体例子吗 + +当我们在构建机器学习模型时,我们需要选择哪些数据特征用于训练模型。在这个过程中,我们需要考虑每个特征对模型的贡献以及它们之间的相关性。 + +特征选择、特征提取和特征工程是一些常用的方法,用于确定不同数据的权重以及如何将它们组合在一起。以下是一些具体的例子: + +1. 特征选择 + +特征选择是一种通过选择最具有预测能力的特征来减少特征数量的技术。这有助于提高模型的准确性并减少过拟合。一些常用的特征选择方法包括卡方检验、皮尔逊相关系数、互信息等。这些方法可以帮助确定哪些特征对于预测目标变量是有用的,从而决定不同数据的权重。 + +2. 特征提取 + +特征提取是一种将原始数据转换为更具有预测效力的特征的技术。例如,在图像识别问题中,我们可以使用卷积神经网络(CNN)来提取图像的特征,然后将这些特征用于训练分类器。特征提取可以帮助我们确定哪些特征对于预测目标变量是最有用的,并从原始数据中提取出这些特征,从而确定不同数据的权重。 + +3. 特征工程 + +特征工程是一种将原始数据转换为更有意义的特征的技术。例如,在自然语言处理中,我们可以使用词袋模型来将文本转换为向量表示,并使用TF-IDF等方法来加权不同的单词。在这种情况下,我们可以将不同单词的权重作为不同数据的权重。特征工程可以帮助我们确定哪些特征对于预测目标变量是最有用的,并将这些特征组合在一起以确定不同数据的权重。 + +## 你能否提供一些机器学习模型中常用的特征选择方法? + +当构建机器学习模型时,特征选择是一个重要的步骤。特征选择的目的是从原始数据中选择有用的特征,以提高模型的准确性和泛化性能。以下是一些常用的特征选择方法: + +1. 方差选择法 + +方差选择法是一种过滤方法,它基于特征的方差来选择特征。如果一个特征的方差很小,那么该特征对目标变量的预测能力可能很低。因此,方差选择法认为这些特征是不重要的,应该被删除。 + +2. 相关系数法 + +相关系数法是一种过滤方法,它基于特征与目标变量之间的相关性来选择特征。如果一个特征与目标变量之间的相关性很小,那么该特征对目标变量的预测能力可能很低。因此,相关系数法认为这些特征是不重要的,应该被删除。 + +3. 卡方检验 + +卡方检验是一种过滤方法,它基于特征与目标变量之间的独立性来选择特征。卡方检验的基本思想是,如果一个特征与目标变量之间是独立的,那么它对目标变量的预测能力可能很低。因此,卡方检验认为这些特征是不重要的,应该被删除。 + +4. 互信息 + +互信息是一种过滤方法,它基于特征与目标变量之间的信息量来选择特征。互信息的基本思想是,如果一个特征与目标变量之间的信息量很大,那么它对目标变量的预测能力可能很高。因此,互信息认为这些特征是重要的,应该被保留。 + +5. Lasso回归 + +Lasso回归是一种包装方法,它通过对特征进行稀疏化来选择特征。Lasso回归的基本思想是,如果一个特征对目标变量的预测能力很低,那么它在Lasso回归中的系数可能很小,从而被压缩为0。因此,Lasso回归认为这些特征是不重要的,应该被删除。 + +以上是一些常用的特征选择方法,当然还有其他的方法,如随机森林、主成分分析等。在实践中,应根据数据集的特点和问题的需求来选择合适的特征选择方法。 + +## 特征工程这一步涉及到的信号处理技术和统计学方法有哪些呢 + +特征工程是机器学习模型构建过程中非常重要的一步,它涉及到从原始数据中提取有意义的特征,以提高模型的准确性和泛化性能。在特征工程过程中,信号处理技术和统计学方法都具有重要的作用。以下是一些常用的信号处理技术和统计学方法: + +1. 时频分析 + +时频分析是一种将信号在时间和频率上分解的技术。在特征工程中,时频分析可以用来提取信号的频域特征,如功率谱、频谱峰值等。常用的时频分析方法包括傅里叶变换、小波变换等。 + +2. 滤波 + +滤波是一种将信号的某些频率成分去除或增强的技术。在特征工程中,滤波可以用来去除噪声、减少信号中的干扰等。常用的滤波方法包括低通滤波、高通滤波、带通滤波等。 + +3. 峰值检测 + +峰值检测是一种从信号中提取峰值或极值的技术。在特征工程中,峰值检测可以用来提取信号的峰值或极值,例如心电图中的QRS波群。常用的峰值检测方法包括阈值法、斜率法、二阶导数法等。 + +4. 统计分析 + +统计分析是一种从数据中提取统计特征的技术。在特征工程中,统计分析可以用来提取数据的分布、中心趋势、离散程度等信息。常用的统计分析方法包括均值、标准差、方差、偏度、峰度等。 + +5. 主成分分析 + +主成分分析是一种从高维数据中提取主要特征的技术。在特征工程中,主成分分析可以用来降维和提取主要特征,以减少数据的复杂性和冗余性。常用的主成分分析方法包括奇异值分解、特征值分解等。 + +以上是一些常用的信号处理技术和统计学方法,在实际应用中需要结合具体问题和数据特点来选择合适的方法。 + +## 特征工程这一步涉及到的深度学习方法包括哪些,请至少列出20种,并详细说明他们的优劣势和使用情况(尤其是在风机上) + +特征工程是机器学习模型构建中非常重要的一步,涉及到从原始数据中提取有意义的特征。在特征工程中,深度学习方法可以用来自动地学习数据中的特征,并提高模型的准确性和泛化能力。以下是一些常见的深度学习方法,以及它们在风机上的应用情况、优劣势。 + +1. 卷积神经网络(Conv[olutional Neural Network](),[CNN]()):主要应用于图像和视频处理任务,在风机中可以用于图像数据的分类和检测。优势在于能够自动提取图像的特征,但需要大量的数据和计算资源。 +2. 长短时记忆网络(Long Short-Term Memory,[LSTM]()):是一种特殊的RNN结构,能够有效地处理长期依赖性问题,在风机中可以用于时间序列数据的建模和预测。优势在于能够有效地解决梯度消失和梯度爆炸等问题,但需要更多的计算资源。 +3. 自编码器(Autoencoder,AE):主要用于特征提取和降维,在风机中可以用于提取信号的特征表示。优势在于能够自动地学习数据的特征表示,但需要大量的数据和计算资源。 +4. 生成对抗网络(Gener[ative Adversarial Network](),GAN):主要用于生成新的数据,在风机中可以用于生成合成数据,如合成风速、温度等。优势在于能够生成逼真的合成数据,但需要更多的计算资源和优化技巧。 +5. 注意力机制(Attention Mechanism):主要用于提高模型的准确性和泛化能力,在风机中可以用于对重要特征的加权。优势在于能够提高模型的准确性和泛化能力,但需要更多的计算资源。 +6. 深度置信网络(Deep Belief Network,DBN):主要用于特征提取和分类,在风机中可以用于信号的分类和故障诊断。优势在于能够自动地学习数据的特征表示和分类器,但需要大量的数据和计算资源。 +7. 变分自编码器(Variational Autoencoder,VAE):主要用于生成新的数据和特征提取,在风机中可以用于合成数据和信号的降维。优势在于能够自动地学习数据的特征表示和生成器,但需要更多的计算资源。 +8. 深度强化学习(Deep [Reinforcement Learning](),DRL):主要用于处理决策问题,在风机中可以用于优化风机的控制策略。优势在于能够自动地学习最优的控制策略,但需要更多的计算资源和优化技巧。 +9. 对抗训练(Adversarial Training):主要用于提高模型的鲁棒性和泛化能力,在风机中可以用于提高模型的抗干扰能力。优势在于能够提高模型的鲁棒性和泛化能力,但需要更多的计算资源和优化技巧。 +10. 循环神经网络(Recurrent Neural Network,RNN):是一种专门用于处理序列数据的神经网络结构,在风机中可以用于时间序列数据的建模和预测。优势在于能够考虑时间序列数据的时序关系和长期依赖性,但需要更多的计算资源和调整超参数。对于风机中的数据,RNN可以用于预测风机的状态和剩余寿命,以及识别故障模式和异常情况。例如,可以使用LSTM或GRU等常见的RNN变体来构建预测模型,并对输入数据进行适当的特征工程和预处理。 +11. 卷积自编码器(Convolutional Autoencoder,CAE):是一种自编码器,专门用于图像处理,在风机中可以用于图像数据的降噪和特征提取。优势在于能够自动地学习图像的特征表示和降噪,但需要更多的计算资源。 +12. 前馈神经网络(Feedforward Neural Network,FNN):是一种最基本的神经网络结构,在风机中可以用于分类和回归任务。优势在于简单易懂,但需要更多的数据和调整超参数。 +13. 残差网络([Residual Network](),[ResNet]()):是一种特殊的神经网络结构,主要用于解决梯度消失问题,在风机中可以用于信号分类和故障诊断。优势在于能够解决梯度消失问题和提高模型的准确性,但需要更多的计算资源。 +14. 双向循环神经网络(Bidirectional [Recurrent Neural Network](),BiRNN):是一种RNN结构的变体,能够同时考虑过去和未来的信息,在风机中可以用于时间序列数据的建模和预测。优势在于能够同时考虑过去和未来的信息,但需要更多的计算资源。 +15. 深度卷积生成对抗网络(Deep [Convolutional Generative Adversarial Network](),DCGAN):是一种特殊的GAN结构,主要用于图像生成,在风机中可以用于生成合成图像数据。优势在于能够生成高质量的合成图像,但需要更多的计算资源和优化技巧。 +16. 长短时注意力网络(Long Short-Term Attention Network,LSTAN):是一种结合了LSTM和注意力机制的神经网络结构,在风机中可以用于时间序列数据的建模和预测。优势在于能够同时考虑时间序列数据和关键特征,但需要更多的计算资源。 +17. 神经网络集成([Neural Network]() Ensemble):是一种将多个神经网络模型组合在一起的方法,在风机中可以用于提高模型的准确性和鲁棒性。优势在于能够综合多个模型的优点,但需要更多的计算资源和数据。 +18. 深度强化学习(Deep [Reinforcement Learning](),DRL):是一种结合了深度学习和强化学习的方法,在风机中可以用于优化控制策略和调节参数。优势在于能够自动学习最优策略和调节参数,但需要更多的计算资源和调整超参数。 +19. 变分自编码器(Variational Autoencoder,VAE):是一种自编码器,能够生成新的样本,用于数据增强,在风机中可以用于数据增强和特征提取。优势在于能够生成高质量的合成数据,但需要更多的计算资源和优化技巧。 + +以上是一些常见的深度学习方法在特征工程中的应用,不同的方法适用于不同的任务和数据。在风机上,常见的任务包括信号分类、故障诊断、预测和控制优化等。针对不同的任务和数据,可以选择不同的方法来进行特征工程和模型训练。 + +## 特征工程这一步涉及的机器学习算法,在风机上的应用以及优缺点 + +特征工程是机器学习的重要步骤,它主要是通过对原始数据进行处理和转换,提取有用的信息和特征,以便机器学习算法能够更好地进行模型训练和预测。在风机领域,特征工程通常包括信号处理、统计分析、频谱分析、小波变换、特征选择等方法。以下是一些常见的机器学习算法,在风机上的应用以及优缺点: + +1. 支持向量机(Support [Vector Machine](),SVM):SVM是一种常见的监督学习算法,在风机中可以用于分类和回归任务,例如故障诊断和剩余寿命预测。SVM的优点在于泛化性能好、适用于高维数据和非线性数据,但需要适当调整超参数和处理缺失数据。 +2. 决策树(Decision Tree):决策树是一种基于树形结构的分类和回归算法,在风机中可以用于故障诊断和异常检测等任务。决策树的优点在于易于理解和解释、能够处理非线性数据和缺失数据,但容易过拟合和不稳定。 +3. 随机森林(Random Forest):随机森林是一种基于多个决策树的集成学习算法,在风机中可以用于故障诊断和预测任务。随机森林的优点在于具有较高的准确性和泛化能力、能够处理高维数据和缺失数据,但需要更多的计算资源和时间。 +4. 神经网络(Neural Network):神经网络是一种基于生物神经元结构的人工智能算法,在风机中可以用于故障诊断、剩余寿命预测和控制等任务。神经网络的优点在于能够处理复杂的非线性数据和时序数据、具有良好的泛化性能和自适应性,但需要更多的计算资源和训练时间。 +5. 梯度提升决策树(Gradient Boosting [Decision Tree](),GBDT):GBDT是一种基于决策树的集成学习算法,在风机中可以用于故障诊断和预测任务。GBDT的优点在于能够处理高维数据和非线性数据、具有较高的准确性和泛化能力,但需要较多的计算资源和时间。 + +总之,在风机领域,不同的机器学习算法具有各自的优缺点,需要根据具体的应用场景选择合适的算法和特征工程方法,以达到最佳的预测性能和效果。 + +## 还有一些其他的机器学习算法吗 + +是的,还有其他一些常见的机器学习算法,以下是一些例子: + +6. K近邻算法(K-[Nearest Neighbors](),KNN):KNN是一种基于实例的非参数算法,在风机领域可用于故障诊断和分类任务。KNN的优点在于简单易用、不需要训练过程,但需要适当的距离度量和选择K值。 +7. 贝叶斯分类器([Bayesian Classifier]()):贝叶斯分类器是一种基于贝叶斯定理的分类算法,可用于风机中的故障诊断和分类任务。贝叶斯分类器的优点在于具有较高的准确性和泛化能力、能够处理高维数据和缺失数据,但需要适当的假设和模型选择。 +8. 主成分分析(Principal Component Analysis,PCA):PCA是一种常见的降维算法,可用于风机中的特征提取和可视化。PCA的优点在于能够提取数据的主要特征和结构、降低模型复杂度和计算成本,但可能会损失一些信息和解释性。 +9. 独立成分分析(Independent [Component Analysis](),ICA):ICA是一种基于统计学的盲源分离算法,可用于风机中的信号分离和特征提取。ICA的优点在于能够提取数据的独立成分和特征、对噪声和干扰具有较好的鲁棒性,但需要适当的假设和调整参数。 +10. 聚类分析(Cluster Analysis):聚类分析是一种无监督学习算法,可用于风机中的异常检测和行为分析。聚类分析的优点在于能够发现数据的内在结构和相似性、不需要先验知识和标记,但需要适当的距离度量和聚类方法。 +11. 联合概率模型(Joint Probability Model,JPM):JPM是一种基于概率模型的机器学习算法,可用于风机中的异常检测和故障诊断等任务。JPM的优点在于能够建立数据的联合概率分布、对复杂数据具有较好的建模能力,但需要适当的假设和参数估计。 +12. 马尔可夫链蒙特卡罗([Markov Chain Monte Carlo](),MCMC):MCMC是一种基于马尔可夫链的采样算法,可用于风机中的贝叶斯推断和模型拟合。MCMC的优点在于能够处理高维参数空间和复杂的后验分布、对参数估计具有较好的收敛性和精度,但需要适当的调整步长和采样次数。 +13. 集成学习(Ensemble Learning):集成学习是一种基于多个模型的组合算法,可用于风机中的分类、回归和聚类等任务。集成学习的优点在于能够减少模型的方差和偏差、提高模型的准确性和泛化能力,但需要适当的模型选择和集成方式。 +14. 深度学习(Deep Learning):深度学习是一种基于多层神经网络的机器学习算法,可用于风机中的故障诊断、剩余寿命预测和控制等任务。深度学习的优点在于能够处理复杂的非线性数据和时序数据、具有良好的泛化性能和表征能力,但需要更多的计算资源和训练时间。 + +## 这么多模型,应该怎么选择合适的机器学习算法呢 + +选择合适的机器学习算法需要考虑多个因素,包括问题类型、数据特征、计算资源和时间成本等。以下是一些选择机器学习算法的一般性建议: + +1. 确定问题类型:首先需要确定问题类型,例如分类、回归、聚类、异常检测等。不同的问题类型需要使用不同的算法和评估指标。 +2. 分析数据特征:其次需要分析数据的特征,例如数据的维度、分布、缺失值和噪声等。不同的数据特征需要使用不同的特征工程方法和算法。 +3. 选择合适的算法:根据问题类型和数据特征,选择合适的机器学习算法。例如,对于分类问题和高维数据,可以考虑使用支持向量机(SVM)或神经网络;对于聚类问题和非线性数据,可以考虑使用K均值聚类或高斯混合模型(GMM)等。 +4. 调整参数和超参数:对于选择的算法,需要适当调整参数和超参数,以达到最佳的预测性能和效果。例如,对于SVM算法,需要选择合适的核函数和正则化参数;对于神经网络算法,需要选择合适的层数、节点数和激活函数等。 +5. 评估和比较算法:最后需要评估和比较不同的算法和模型,以选择最佳的算法和特征工程方法。常用的评估指标包括准确率、召回率、精度、F1值等。同时,需要注意过拟合和欠拟合问题,避免模型复杂度过高或过低。 + +总之,在选择机器学习算法时,需要根据具体的问题类型和数据特征,结合实际情况和经验选择合适的算法和特征工程方法,并进行适当调整和优化,以获得最佳的预测性能和效果。 + +## 确定问题类型和分析数据特征看上去很关键。请问你可以列出这两步需要考虑的所有组合,并且为这些组合提供合适的机器学习算法吗 + +确定问题类型和分析数据特征确实是选择合适的机器学习算法的关键步骤。以下是一些常见的问题类型和数据特征的组合,以及相应的机器学习算法建议: + +1. 二分类问题和结构化数据:对于二分类问题和结构化数据,可以考虑使用逻辑回归、决策树、随机森林等算法。 +2. 多分类问题和结构化数据:对于多分类问题和结构化数据,可以考虑使用朴素贝叶斯、K近邻、支持向量机(SVM)等算法。 +3. 回归问题和结构化数据:对于回归问题和结构化数据,可以考虑使用线性回归、岭回归、Lasso回归等算法。 +4. 聚类问题和非结构化数据:对于聚类问题和非结构化数据,可以考虑使用K均值聚类、层次聚类、高斯混合模型(GMM)等算法。 +5. 异常检测问题和非结构化数据:对于异常检测问题和非结构化数据,可以考虑使用离群点检测、孤立森林、基于密度的异常检测等算法。 +6. 时序预测问题和序列数据:对于时序预测问题和序列数据,可以考虑使用循环神经网络(RNN)、长短时记忆网络(LSTM)、卷积神经网络(CNN)等算法。 +7. 图像识别问题和图像数据:对于图像识别问题和图像数据,可以考虑使用卷积神经网络(CNN)、深度残差网络(ResNet)、网络中的网络(NiN)等算法。 +8. 自然语言处理问题和文本数据:对于自然语言处理问题和文本数据,可以考虑使用循环神经网络(RNN)、长短时记忆网络(LSTM)、卷积神经网络(CNN)等算法。 + +需要注意的是,这些组合和算法建议仅供参考,实际选择算法需要结合具体情况进行综合考虑和比较。 + +## 我想了解风机二分类问题,应该怎么下手,最好提供一些机器学习和深度学习的算法案例 + +针对风机二分类问题,可以采用以下步骤: + +1. 数据预处理 + +首先需要对数据进行预处理,包括数据清洗、数据可视化、数据归一化等,以便更好地应用机器学习算法。特别是对于风机的传感器数据,需要对数据进行去噪、异常值处理、缺失值填充等操作。 + +2. 特征提取 + +对于风机数据,可以提取多种特征,包括振动信号、温度、湿度等。特别是针对振动信号,可以提取频域特征、时域特征、小波包能量等特征。 + +3. 机器学习算法 + +针对风机二分类问题,可以采用以下机器学习算法: + +* 逻辑回归:适用于二分类问题,可以通过线性分类器进行分类。 +* 支持向量机(SVM):适用于处理高维度的特征向量,可以通过核函数进行非线性分类。 +* 随机森林:适用于处理具有多个特征的数据,可以通过集成多个决策树进行分类。 +* GBDT:适用于处理非线性数据,可以通过集成多个决策树进行分类。 + +4. 深度学习算法 + +除了机器学习算法,深度学习算法也可以应用于风机二分类问题中。以下是一些适用于风机二分类问题的深度学习算法: + +* 卷积神经网络(CNN):适用于图像、信号等数据的分类,可以通过卷积操作提取数据的特征。 +* 循环神经网络(RNN):适用于序列数据(如时间序列数据)的分类,可以学习到序列数据中的时间依赖关系。 +* 长短时记忆网络(LSTM):适用于处理序列数据的分类,可以记住长时间间隔的依赖关系。 +* 自编码器(Autoencoder):可以学习到数据的压缩表示,是进行无监督学习的一种方法。 + +需要注意的是,机器学习算法和深度学习算法的选择需要结合具体问题进行综合考虑和比较,并且需要进行模型的调参和优化,以提高分类的准确性和效率。同时,对于风机数据的预处理和特征提取也是影响分类效果的重要因素。 + +## 我对随机深林的算法比较感兴趣,请问您接下来我应该如何进行参数调呢 + +针对随机森林算法的参数调优,以下是一些常用的方法和技巧: + +1. n_estimators:这个参数指的是随机森林中决策树的数量。一般来说,n_estimators越大,模型的性能会越好,但同时也会增加模型的计算时间。因此,需要通过交叉验证等方法来确定合适的n_estimators值。 +2. max_depth:这个参数指的是决策树的最大深度。一般来说,设置max_depth可以防止过拟合,但同时也会使模型的泛化能力降低。因此,需要通过交叉验证等方法来确定合适的max_depth值。 +3. min_samples_split:这个参数指的是分裂一个内部节点需要的最小样本数。一般来说,这个值越小,模型会越容易过拟合。因此,需要通过交叉验证等方法来确定合适的min_samples_split值。 +4. min_samples_leaf:这个参数指的是每个叶子节点需要的最小样本数。一般来说,这个值越小,模型会越容易过拟合。因此,需要通过交叉验证等方法来确定合适的min_samples_leaf值。 +5. max_features:这个参数指的是在寻找最佳分割点时,随机选择的特征数量。一般来说,max_features越小,模型会越容易过拟合。因此,需要通过交叉验证等方法来确定合适的max_features值。 + +以上是一些常用的调参方法和技巧,需要注意的是,在调参过程中需要避免过拟合问题,并且需要结合具体问题进行综合考虑和比较。同时,可以采用网格搜索、随机搜索等方法来进行参数搜索和优化。 + +## 通过交叉验证等方法来确定合适的n_estimators值是什么意思呢 + +在随机森林算法中,n_estimators是指随机森林中包含的决策树的数量。n_estimators越大,模型的性能通常会更好,但同时也会增加模型的计算时间和存储空间。因此,需要通过交叉验证等方法来确定合适的n_estimators值,以达到平衡模型性能和效率的目的。 + +交叉验证是一种常用的机器学习方法,可以将数据集划分为训练集和验证集,并重复进行多次,以评估模型的性能和泛化能力。在确定n_estimators值时,可以采用交叉验证来评估不同的n_estimators值对模型性能的影响。具体来说,可以将数据集划分为训练集和验证集,然后针对不同的n_estimators值训练多个模型,并在验证集上进行评估,得到不同n_estimators值下的模型性能指标。然后,可以比较不同n_estimators值的性能指标,选择最优的n_estimators值作为最终模型的参数。 + +需要注意的是,交叉验证的结果具有一定的随机性,因此需要进行多次交叉验证,并取平均值来获得更可靠的结果。同时,为了避免过拟合,还需要在每次交叉验证中随机选择训练集和验证集,并对模型进行平均化处理,以提高模型的泛化能力。 + +## 还不是很懂,看上去参数的选择都需要设计到交叉验证的方法,请问这种方法运行开销是怎么样的呢 + +是的,参数选择通常需要采用交叉验证等方法来评估不同参数值对模型性能的影响。交叉验证方法的运行开销取决于数据集的大小和交叉验证的折数等因素。 + +交叉验证方法的一般流程是将数据集划分为k个互不重叠的子集,然后选择其中一个子集作为验证集,剩余的k-1个子集作为训练集,进行模型训练和测试,并记录性能指标。然后,循环k次,每次选择不同的验证集,并进行模型训练和测试,最后对性能指标进行平均或求和,得到最终的性能指标。因此,交叉验证的运行开销取决于数据集的大小和交叉验证的折数。通常来说,数据集越大,交叉验证的开销就越大。 + +在随机森林算法中,交叉验证通常用于选择合适的参数值,例如n_estimators、max_depth、min_samples_split等。具体来说,可以采用网格搜索或随机搜索等方法,在不同的参数范围内进行搜索,并通过交叉验证方法评估不同参数值对模型性能的影响。搜索过程的开销取决于参数范围的大小和搜索方法的复杂度等因素。 + +总的来说,交叉验证方法的运行开销较大,但可以有效评估模型的性能和泛化能力,提高模型的准确性和可靠性。在实际应用中,需要根据具体情况选择合适的交叉验证方法和参数搜索方法,以达到平衡模型性能和效率的目的。 + +## 看上去,参数搜素可以减少运行开销,请问您可以提供具体的参数搜索的方法吗 + +是的,参数搜索可以通过精心设计的搜索策略来减少运行开销,并找到最优的参数组合。以下是几种常用的参数搜索方法: + +1. 网格搜索([Grid Search]()):网格搜索是一种基于穷举搜索的方法,通过指定参数的范围和步长,对每个参数进行排列组合,然后在所有参数组合中选择性能最好的一组参数。这种方法简单直观,但搜索空间很大时计算开销很大。 +2. 随机搜索(Random Search):随机搜索是一种基于随机采样的方法,通过指定参数范围和采样分布,随机采样若干次,然后在所有采样中选择性能最好的一组参数。这种方法计算开销较小,但搜索效率可能较低,因为搜索过程是基于随机采样的。 +3. 贝叶斯优化([Bayesian Optimization]()):贝叶斯优化是一种基于高斯过程的方法,通过建立参数空间的概率模型,不断更新模型的先验概率分布,然后根据模型的预测结果选择下一个参数组合,直到找到性能最好的一组参数。这种方法计算开销较大,但搜索效率较高,在高维参数空间中表现良好。 +4. 梯度提升调参(Gradient Boosting Tuning):梯度提升调参是一种基于梯度提升决策树算法的方法,通过不断迭代模型,对每个参数进行微调,然后根据模型的性能指标进行选择,直到找到性能最好的一组参数。这种方法计算开销较大,但搜索效率较高,并且可以避免过度拟合。 + +需要注意的是,不同的参数搜索方法适用于不同的场景,需要根据具体情况进行选择。同时,为了进一步减少运行开销,可以采用并行计算、分布式计算等方法,加速参数搜索的过程。 + +## 请问如何选择最优的搜索方法? + +选择最优的搜索方法需要考虑多个因素,包括搜索空间大小、搜索效率、搜索准确性、计算资源等。以下是几个选择最优搜索方法的建议: + +1. 首先需要根据问题的特性和参数搜索空间的大小来选择搜索方法。如果搜索空间较小,可以考虑网格搜索或随机搜索;如果搜索空间较大,可以考虑贝叶斯优化或梯度提升调参等方法。 +2. 其次需要考虑搜索效率和搜索准确性的平衡。随机搜索和贝叶斯优化等方法可以在较短时间内找到较好的参数组合,但搜索结果可能并不是全局最优解;而网格搜索和梯度提升调参等方法可以较精确地找到全局最优解,但需要较长的时间。 +3. 另外需要考虑计算资源的限制。如果计算资源有限,可以选择随机搜索等方法,通过限制搜索次数或时间来控制计算开销;如果计算资源充足,可以选择网格搜索或贝叶斯优化等方法,以获得更准确的搜索结果。 +4. 最后,需要根据具体情况进行实验和评估,选择最优的搜索方法。可以通过交叉验证等方法,评估不同搜索方法的性能和泛化能力,选择最优的搜索方法并得到最优的参数组合。 + +需要注意的是,选择最优的搜索方法并不是一件容易的事情,需要综合考虑多个因素。同时,搜索方法的选择并不是唯一的,可以根据实际情况进行调整和优化。 + +## 选择了合适的参数之后,一般怎么进行模型训练 + +选择了合适的参数之后,可以使用所选参数来训练模型。以下是一般的模型训练流程: + +1. 数据准备:首先需要准备训练数据和测试数据。训练数据用于模型的训练,测试数据用于模型的验证和评估。可以使用交叉验证等技术来进一步优化数据准备过程。 +2. 模型定义:根据所选的算法和参数,定义模型的结构和参数设置。可以使用现有的深度学习框架(如TensorFlow、PyTorch等)来定义模型。 +3. 损失函数定义:根据具体问题,选择相应的损失函数(如交叉熵、均方误差等)来描述模型的性能指标。 +4. 模型训练:使用训练数据来训练模型。训练过程中,模型根据损失函数的反馈进行参数更新,不断优化模型的性能指标。 +5. 模型验证:使用测试数据来验证模型的性能。验证过程中,可以计算模型的准确率、精度、召回率等指标,评估模型的性能和泛化能力。 +6. 模型调整:根据验证结果,调整模型的参数设置和结构,优化模型的性能。可以多次进行模型训练和验证,直到获得满意的结果。 +7. 模型保存:保存训练好的模型,方便后续的模型预测和应用。 + +需要注意的是,模型训练过程中需要进行参数初始化、梯度计算、反向传播、优化器选择等操作。同时,为了防止过拟合,需要使用正则化技术、数据增强等方法进行模型调整和优化。 + +## 看上去损失函数很重要,在随机深林的算法中,Ibanez是怎么定义这个损失函数的呢 + +Ibanez提出的随机森林算法中,损失函数是构建随机森林模型的一个重要组成部分,它被用来度量模型预测结果与真实标签之间的差距。Ibanez使用的损失函数是分类问题中的交叉熵损失函数,它的定义如下: + +$$ +H(p,q) = - \sum_{i=1}^m p_i \log q_i +$$ + +其中,$p_i$表示真实标签的概率分布,$q_i$表示模型预测的概率分布。交叉熵损失函数可以度量模型预测结果与真实标签之间的差距,是分类问题中常用的损失函数之一。当模型的预测结果与真实标签完全一致时,交叉熵损失函数取最小值0;当模型的预测结果与真实标签完全不一致时,交叉熵损失函数取最大值。 + +在随机森林算法中,对于每个决策树,都会使用交叉熵损失函数来度量分裂节点的优劣,选择最优的特征和阈值进行分裂。在多棵决策树的集成中,随机森林使用投票的方式来进行分类预测,将每个决策树的预测结果进行投票,得到最终的分类结果。在投票过程中,每个决策树的权重是由交叉熵损失函数来确定的,交叉熵损失函数越小的决策树,权重越大,对最终的分类结果影响越大。 + +## tell me more + +除了交叉熵损失函数,随机森林算法中还可以使用其他的损失函数来度量模型的性能和预测结果与真实标签之间的差距。常见的损失函数包括: + +1. 均方误差([Mean Squared Error](),MSE):均方误差是回归问题中常用的损失函数,它度量模型预测结果与真实标签之间的平均差距。均方误差越小,模型的预测结果越接近真实标签。在随机森林算法中,可以使用均方误差来选择最优的特征和阈值进行分裂。 +2. 绝对误差(Mean Absolute Error,MAE):绝对误差是回归问题中另一种常用的损失函数,它度量模型预测结果与真实标签之间的平均绝对差距。与均方误差不同,绝对误差对误差的大小不敏感,更加稳健。在随机森林算法中,可以使用绝对误差来选择最优的特征和阈值进行分裂。 +3. 基尼不纯度(Gini Impurity):基尼不纯度是分类问题中常用的损失函数之一,它度量随机选择一个样本,它被错误分类为其它类别的概率。基尼不纯度越小,表示样本的纯度越高,模型的分类效果越好。在随机森林算法中,可以使用基尼不纯度来选择最优的特征和阈值进行分裂。 + +随机森林算法中的损失函数选择依赖于具体的问题和数据集,不同的损失函数可能导致不同的模型性能和预测结果。因此,在模型训练过程中,需要根据具体问题和数据集选择合适的损失函数,对模型进行优化和调整。 + +## 模型的性能指标如何选取 + +模型的性能指标应该根据具体的问题和任务进行选择。不同的问题和任务可能需要不同的性能指标,因此需要根据具体情况进行选择。 + +以下是一些常见的模型性能指标及其适用范围: + +1. 准确率(Accuracy):准确率是最常用的模型性能指标之一,它度量模型正确分类的样本占总样本数的比例。准确率适用于类别平衡的问题,但对于类别不平衡的问题,准确率可能会忽略掉少数类别,因此需要使用其他指标来衡量模型性能。 +2. 精度(Precision):精度是指在所有被预测为正例的样本中,实际为正例的样本占比。精度适用于关注预测结果为正例的问题,如垃圾邮件识别等。 +3. 召回率(Recall):召回率是指在所有实际为正例的样本中,被预测为正例的样本占比。召回率适用于关注所有正例被正确预测的问题,如疾病诊断等。 +4. F1值(F1 Score):F1值是精度和召回率的调和平均数,它综合考虑了精度和召回率的性能,适用于关注精度和召回率的平衡问题。 +5. ROC曲线和AUC值(ROC curve and AUC):ROC曲线展示了模型预测结果的真正率和假正率之间的关系,AUC值是ROC曲线下的面积,表示模型分类效果的综合性能,适用于关注模型分类效果全面性的问题。 +6. 平均绝对误差(Mean Absolute Error,MAE):MAE是回归问题中常用的性能指标,它度量模型预测结果与真实标签之间的平均绝对差距,适用于回归问题的性能评估。 + +在选择模型性能指标时,需要根据具体问题和任务选择合适的指标,并综合考虑模型性能和应用场景的需求,进行权衡和选择. + +## 在随机深林中,如何构建性能指标,根据损失函数更新参数呢 + +在随机深林中,通常使用交叉熵损失函数作为性能指标来度量模型的预测性能。对于每个决策树,都会使用交叉熵损失函数来度量分裂节点的优劣,选择最优的特征和阈值进行分裂。在多棵决策树的集成中,随机深林使用投票的方式来进行分类预测,将每个决策树的预测结果进行投票,得到最终的分类结果。 + +在模型训练过程中,随机深林使用梯度提升(Gradient Boosting)算法来更新模型参数。梯度提升算法通过迭代的方式,将每个决策树的预测结果与真实标签之间的误差作为损失函数,更新模型参数,使得模型能够更好地拟合训练数据。 + +具体来说,梯度提升算法通过以下步骤来更新模型参数: + +1. 初始化模型参数。随机深林通常使用浅层决策树作为基分类器,对于每个决策树,需要确定其最大深度、节点分裂的最小样本数等参数。 +2. 对于每个决策树,计算其预测结果与真实标签之间的误差。随机深林使用交叉熵损失函数来度量分类预测的误差,使用均方误差(MSE)来度量回归预测的误差。 +3. 根据误差计算每个样本的梯度。对于分类问题,样本的梯度是预测结果与真实标签之间的差值,对于回归问题,样本的梯度是预测结果与真实标签之间的差值。 +4. 使用梯度下降算法更新模型参数。随机深林使用梯度提升算法来更新模型参数,将每个决策树的预测结果与真实标签之间的误差作为损失函数,通过梯度下降算法来更新模型参数。 +5. 重复步骤2-4,直到达到指定的迭代次数或达到收敛条件为止。 + +在随机深林中,由于每个决策树都是独立训练的,因此可以使用并行化的方式来加速模型训练,提高模型的性能和效率。 + +## Tell me more. + +随机深林(Random Forest)是一种集成学习算法,它通过组合多个决策树来进行分类和回归预测。随机深林中的每个决策树都是独立训练的,每棵树都是通过随机选择特征和样本进行训练的,从而增强了模型的泛化能力,避免了过拟合问题。 + +随机深林的核心思想是基于袋装法(Bagging)和随机特征选择(Random [Feature Selection]())的组合。具体来说,随机深林采用以下步骤来构建模型: + +1. 从训练数据集中使用有放回抽样的方式,随机选择一定数量的样本,构建一组新的训练集。 +2. 对于每个新的训练集,使用随机特征选择的方式,随机选择一定数量的特征进行训练,构建一棵决策树。 +3. 重复步骤1和步骤2,构建多棵决策树,形成随机深林。 +4. 对于分类问题,随机深林使用投票的方式来进行分类预测,将每个决策树的预测结果进行投票,得到最终的分类结果;对于回归问题,随机深林使用平均值的方式来进行回归预测,将每个决策树的预测结果进行平均,得到最终的回归结果。 + +与单棵决策树相比,随机深林具有以下优点: + +1. 随机深林能够有效地避免过拟合问题,通过组合多个决策树进行预测,能够降低模型的方差,提高模型的泛化能力。 +2. 随机深林能够处理高维数据和噪声数据,随机特征选择的方式可以有效地减少冗余特征和噪声特征的影响,从而提高模型的准确性。 +3. 随机深林能够并行化处理,每棵决策树都是独立训练的,可以使用多线程或分布式计算的方式加速模型训练,提高模型的效率。 + +总之,随机深林是一种高效、准确、稳定的集成学习算法,被广泛应用于分类、回归、特征选择等领域。 + +## 更新得到新的参数之后,怎么确定自己的模型已经是最优的呢 + +在随机深林中,确定模型是否已经达到最优状态通常需要进行模型评估。模型评估可以通过使用交叉验证方法来进行,常见的交叉验证方法包括K折交叉验证和留一交叉验证。 + +K折交叉验证是指将原始数据集分成K份,每次选取其中的K-1份作为训练集,剩余的1份作为验证集,重复K次,得到K个模型的预测结果,最终将K个模型的预测结果进行平均或投票,得到最终的模型预测结果。 + +留一交叉验证是指将原始数据集中的每个样本都作为验证集,其余样本作为训练集,重复N次,得到N个模型的预测结果,最终将N个模型的预测结果进行平均或投票,得到最终的模型预测结果。 + +在交叉验证过程中,可以使用常见的评估指标来评估模型的性能,如准确率、精确率、召回率、F1值等。通常情况下,选择具有最高评估指标的模型作为最优模型,但也可以根据具体应用场景和需求进行选择。 + +此外,还可以使用网格搜索(Grid Search)等方法来进行模型调优,通过在模型参数空间中搜索,找到最优的模型参数组合,从而提高模型的性能和泛化能力。 + +总之,在随机深林中,确定最优模型需要进行模型评估和调优,根据具体应用场景和需求进行选择。 + +## 请问如何提高模型的泛化能力? + +在机器学习中,提高模型的泛化能力是非常重要的,因为泛化能力是指模型在未见过的数据上的表现能力,是衡量模型优劣的重要指标之一。以下是提高模型泛化能力的一些方法: + +1. 增加训练数据量:增加训练数据量可以帮助模型更好地学习数据的特征,从而提高模型的泛化能力。 +2. 数据预处理:数据预处理包括数据清洗、特征选择、特征缩放等,可以帮助模型更好地学习数据的特征,从而提高模型的泛化能力。 +3. 减少模型复杂度:过于复杂的模型容易过拟合,可以通过减少模型的复杂度来提高模型的泛化能力,如降低模型的层数、节点数等。 +4. 正则化:正则化是通过增加模型复杂度惩罚项或限制模型参数的范围来防止过拟合的一种方法,可以提高模型的泛化能力。 +5. 集成学习:集成学习是通过组合多个模型来进行预测,可以有效地降低模型的方差,提高模型的泛化能力。 +6. Dropout技术:Dropout技术是一种正则化方法,通过在模型训练时随机关闭一定比例的神经元,可以有效地防止模型过拟合,提高模型的泛化能力。 +7. 交叉验证:交叉验证是一种通过将数据集划分为多个子集来评估模型性能的方法,可以帮助防止模型过拟合,提高模型的泛化能力。 + +总之,提高模型的泛化能力需要采取多种方法,包括增加训练数据量、数据预处理、减少模型复杂度、正则化、集成学习、Dropout技术、交叉验证等,根据具体情况选择合适的方法可以有效地提高模型的泛化能力。 + +## 评估模型泛化能力,有什么具体的指标吗 + +评估模型泛化能力的常用指标包括: + +1. 准确率(Accuracy):准确率指分类模型中正确分类的样本数占总样本数的比例,是最常用的指标之一。 +2. 精确率(Precision):精确率指被模型预测为正类的样本中,真正为正类的样本比例。 +3. 召回率(Recall):召回率指真正为正类的样本中,被模型预测为正类的比例。 +4. F1值(F1-score):F1值是精确率和召回率的调和平均数,综合考虑了二者的表现。 +5. ROC曲线和AUC值:ROC曲线是以假正率(FPR)为横坐标,真正率(TPR)为纵坐标画出的曲线,用于评估二分类模型的性能。AUC值是ROC曲线下的面积,可以用于比较不同模型的性能。 +6. R2值:R2值是用于评估回归模型的性能指标,表示模型预测值与真实值之间的相关性,取值范围为0~1,越接近1表示模型拟合效果越好。 +7. 平均绝对误差(MAE)和均方误差(MSE):MAE和MSE是用于评估回归模型的常用指标,分别表示预测值与真实值之间的平均绝对误差和平均平方误差。 + +总之,评估模型泛化能力需要根据具体的应用场景选择合适的指标,常用的指标包括准确率、精确率、召回率、F1值、ROC曲线和AUC值、R2值、MAE和MSE等 + +## 我有点混淆了,请问性能指标和泛化能力指标的差别 + +性能指标和泛化能力指标都是用于评估机器学习模型的指标,但它们的侧重点不同。 + +性能指标主要用于评估模型在训练集上的表现,是衡量模型是否能够很好地拟合训练数据的指标。常用的性能指标包括模型的准确率、精确率、召回率、F1值、均方误差(MSE)、平均绝对误差(MAE)等。性能指标可以用于评估模型训练的效果和调整模型参数。 + +泛化能力指标主要用于评估模型在未知数据上的表现,即模型是否能够很好地适应新数据。常用的泛化能力指标包括模型的交叉验证结果、测试集上的准确率、AUC值、R2值等。泛化能力指标可以用于评估模型的泛化能力和避免模型的过拟合。 + +综上所述,性能指标和泛化能力指标都是用于评估机器学习模型的指标,但是它们的侧重点不同,性能指标主要用于评估模型在训练集上的表现,而泛化能力指标主要用于评估模型在未知数据上的表现。 + +## 如何在模型训练中平衡性能指标和泛化能力指标? + +在模型训练中平衡性能指标和泛化能力指标是一个非常重要的问题。如果只关注性能指标而忽略泛化能力指标,可能会导致模型过拟合,而如果只关注泛化能力指标而忽略性能指标,可能会导致模型欠拟合。以下是一些平衡性能指标和泛化能力指标的方法: + +1. 数据预处理:数据预处理可以帮助模型更好地学习数据的特征,从而提高模型的性能和泛化能力。例如,可以进行特征选择、特征缩放、数据清洗等操作。 +2. 模型选择和调参:选择合适的模型和调整模型参数可以平衡性能指标和泛化能力指标。需要根据实际情况选择合适的模型和调整超参数,避免模型过拟合或欠拟合。 +3. 正则化技术:正则化技术可以帮助平衡性能指标和泛化能力指标。例如,L1正则化和L2正则化可以控制模型的复杂度,避免模型过拟合。 +4. 交叉验证:交叉验证可以帮助评估模型的泛化能力,避免模型在训练集上过拟合。可以使用K折交叉验证或留一交叉验证等方法。 +5. 集成学习:集成学习可以通过组合多个模型的预测结果来提高模型的性能和泛化能力。例如,可以使用Bagging、Boosting、随机森林等方法。 + +综上所述,平衡性能指标和泛化能力指标需要采取一系列方法,包括数据预处理、模型选择和调参、正则化技术、交叉验证和集成学习等。需要根据实际情况选择合适的方法,从而平衡性能指标和泛化能力指标。 + +## 正则化技术看上去很重要,可以提供更详细的数学定义吗 + +正则化是一种常用的降低模型复杂度的技术,它通过在模型的损失函数中添加一个正则项来惩罚模型的复杂度,从而避免模型过拟合。 + +在线性回归中,正则化通常采用L1正则化和L2正则化。L1正则化将模型的损失函数定义为: + +$$ +\min_{\theta}\frac{1}{2m}\sum_{i=1}^{m}(h_{\theta}(x^{(i)})-y^{(i)})^2+\lambda\sum_{j=1}^{n}|\theta_j| +$$ + +其中,$\theta$是模型的参数,$h_{\theta}(x^{(i)})$是模型对第$i$个样本$x^{(i)}$的预测值,$y^{(i)}$是第$i$个样本的真实值,$m$是样本数,$n$是特征数,$\lambda$是控制正则化强度的超参数。 + +L2正则化将模型的损失函数定义为: + +$$ +\min_{\theta}\frac{1}{2m}\sum_{i=1}^{m}(h_{\theta}(x^{(i)})-y^{(i)})^2+\lambda\sum_{j=1}^{n}\theta_j^2 +$$ + +L1正则化和L2正则化的区别在于正则项的形式不同。L1正则化的正则项是所有参数的绝对值之和,它倾向于将一些参数压缩到0,从而产生稀疏性,可以用于特征选择和模型压缩;而L2正则化的正则项是所有参数的平方和,它倾向于将所有参数都缩小,从而产生平滑性。 + +在深度学习中,正则化也是一种常用的技术。例如,Dropout正则化可以随机地将一些神经元输出置为0,从而减少了神经元之间的依赖性,避免模型过拟合。Batch Normalization正则化可以通过对每个批次的输入数据进行标准化,从而减少了模型对输入数据的依赖性,提高了模型的泛化能力。 + +综上所述,正则化是一种常用的降低模型复杂度的技术,可以通过在模型的损失函数中添加一个正则项来惩罚模型的复杂度,从而避免模型过拟合。常用的正则化技术包括L1正则化、L2正则化、Dropout正则化和Batch Normalization正则化等。 + +## 如何部署到实际的应用中呢 + +部署机器学习模型到实际的应用中需要考虑多个方面,包括模型选择、数据预处理、模型训练、模型评估、模型部署、性能优化和安全性等问题。以下是一些常用的部署机器学习模型的方法: + +1. 模型选择:需要根据实际情况选择合适的模型,并进行模型训练和评估。可以使用开源的机器学习库,如Scikit-learn、[TensorFlow]()、PyTorch等。 +2. 数据预处理:需要对输入数据进行预处理,包括数据清洗、特征提取、特征缩放等操作。可以使用Pandas、NumPy等库进行数据处理。 +3. 模型训练和评估:需要进行模型训练和评估,选择合适的算法、超参数和评估指标。可以使用交叉验证、网格搜索等技术进行模型优化。 +4. 模型部署:需要将训练好的模型部署到实际的应用中。可以使用各种部署技术,如Docker、Kubernetes等。 +5. 性能优化:需要对模型进行性能优化,包括模型压缩、量化、加速等操作。可以使用TensorRT、TensorFlow Lite等库进行性能优化。 +6. 安全性:需要保证模型的安全性,包括数据隐私、模型盗用等问题。可以使用加密、数据脱敏、模型水印等技术保证模型的安全性。 + +综上所述,部署机器学习模型到实际的应用中需要考虑多个方面,包括模型选择、数据预处理、模型训练、模型评估、模型部署、性能优化和安全性等问题。需要根据实际情况选择合适的技术和方法,从而实现高性能、高可靠性和高安全性的部署。 + +## 需要将训练好的模型部署到实际的应用中。可以使用各种部署技术,如Docker、Kubernetes等。请问可以详细一点吗 + +当需要将训练好的机器学习模型部署到实际的应用中时,通常会选择使用一些容器化技术,如Docker和Kubernetes。这些技术可以帮助开发人员更轻松地管理和部署应用程序和服务,并提供一些额外的好处,例如可扩展性、安全性和可靠性等。 + +下面是一些常用的容器化技术和部署方法: + +1. Docker:Docker是一种流行的开源容器化平台,可以将应用程序和服务打包成容器,从而实现快速、可重复和可移植的部署。可以使用Dockerfile文件定义容器镜像,然后使用Docker命令构建和运行容器。 +2. Kubernetes:Kubernetes是一个开源的容器编排平台,可以管理容器化应用程序和服务的部署、扩展、升级和滚动更新等操作。可以使用Kubernetes的配置文件定义应用程序和服务的部署和管理。 +3. 容器编排工具:除了Kubernetes之外,还有一些其他的容器编排工具,如Docker Compose、[Apache Mesos]()、[HashiCorp]() Nomad等。这些工具可以帮助开发人员更轻松地管理和部署多个容器化应用程序和服务。 +4. 云计算平台:云计算平台,如AWS、Azure、GCP等,提供了各种容器化服务和工具,可以帮助开发人员更轻松地部署和管理容器化应用程序和服务。 + +综上所述,将训练好的机器学习模型部署到实际的应用中,可以使用容器化技术,如Docker和Kubernetes等。这些技术可以帮助开发人员更轻松地管理和部署应用程序和服务,并提供一些额外的好处,例如可扩展性、安全性和可靠性等。需要根据实际情况选择合适的技术和方法,从而实现高性能、高可靠性和高安全性的部署。 + +## 可以把所有的基于机器学习的风机轴承健康监测评估的步骤列一下吗 + +基于机器学习的风机轴承健康监测评估通常包括以下步骤: + +1. 数据收集:收集与风机轴承相关的传感器数据,例如振动、温度、压力等。 +2. 数据预处理:对收集到的数据进行预处理,包括数据清洗、去噪、滤波、降采样等操作,以提高数据质量和降低数据维度。 +3. 特征提取:从预处理后的数据中提取重要的特征,例如时域特征、频域特征、小波特征等。可以使用相关统计学方法、信号处理技术和机器学习算法进行特征提取。 +4. 特征选择:从提取的特征中选择最具有代表性和预测能力的特征,以减少特征维度和提高模型性能。可以使用相关统计学方法、机器学习算法和特征重要性评估等技术进行特征选择。 +5. 模型训练:使用机器学习算法训练模型,例如支持向量机、随机森林、神经网络等。可以使用交叉验证、网格搜索等技术进行模型选择和优化。 +6. 模型评估:对训练好的模型进行评估,例如精度、召回率、F1分数等。可以使用混淆矩阵、ROC曲线、AUC值等指标进行模型评估。 +7. 模型部署:将训练好的模型部署到实际的应用中,例如使用Docker、Kubernetes等技术进行容器化部署。 +8. 实时监测:在实际使用中,对风机轴承进行实时监测,获取新的传感器数据,进行数据预处理、特征提取、特征选择和模型预测等操作,以实现实时健康监测和预测。 + +综上所述,基于机器学习的风机轴承健康监测评估通常包括数据收集、数据预处理、特征提取、特征选择、模型训练、模型评估、模型部署和实时监测等步骤。需要根据实际情况选择合适的技术和方法,从而实现高性能、高可靠性和高安全性的风机轴承健康监测和预测。 + +## 有具体别人做过的案例可以学习吗,比如说发布的文章或者博客 + +是的,有很多机器学习的案例可以学习,以下是一些基于机器学习的风机轴承健康监测评估的案例,供您参考: + +1. "A [Deep Convolutional Neural Network]() Based Method for [Fault Diagnosis]() of Wind Turbine Bearings"(基于深度卷积神经网络的风力涡轮机轴承故障诊断方法):该论文提出了一种基于深度卷积神经网络的轴承故障诊断方法,在实验中取得了良好的效果。论文链接:[https://ieeexplore.ieee.org/document/8353225](https://ieeexplore.ieee.org/document/8353225) +2. "Wind turbine gearbox fault diagnosis based on [PCA]() and [SVM]()"(基于PCA和SVM算法的风力涡轮机齿轮箱故障诊断):该论文提出了一种基于主成分分析和支持向量机算法的齿轮箱故障诊断方法,在实验中取得了良好的效果。论文链接:[https://www.sciencedirect.com/science/article/pii/S1876610213002019](https://www.sciencedirect.com/science/article/pii/S1876610213002019) +3. "Wind turbine [fault detection]() using [machine learning algorithms]()"(利用机器学习算法进行风力涡轮机故障检测):该论文提出了一种基于K近邻算法和支持向量机算法的风力涡轮机故障检测方法,在实验中取得了良好的效果。论文链接:[https://www.sciencedirect.com/science/article/pii/S0378779616302521](https://www.sciencedirect.com/science/article/pii/S0378779616302521) +4. "Wind Turbine Fault Detection and [Diagnosis]() Based on [Machine Learning Techniques]()"(基于机器学习技术的风力涡轮机故障检测和诊断):该博客介绍了一种基于机器学习技术的风力涡轮机故障检测和诊断方法,包括数据预处理、特征提取、特征选择、模型训练和评估等步骤。博客链接:[https://towardsdatascience.com/wind-turbine-fault-detection-and-diagnosis-based-on-machine-learning-techniques-52c0b4f4b4e8](https://towardsdatascience.com/wind-turbine-fault-detection-and-diagnosis-based-on-machine-learning-techniques-52c0b4f4b4e8) + +以上是一些基于机器学习的风机轴承健康监测评估的案例,希望对您有所帮助。 + +## 你提供的链接全都是错误的 diff --git "a/_posts/2023-03-18-\345\234\250\345\215\232\345\256\242\344\270\255\346\267\273\345\212\240\351\237\263\344\271\220.md" "b/_posts/2023-03-18-\345\234\250\345\215\232\345\256\242\344\270\255\346\267\273\345\212\240\351\237\263\344\271\220.md" new file mode 100644 index 0000000000..cebdf45c12 --- /dev/null +++ "b/_posts/2023-03-18-\345\234\250\345\215\232\345\256\242\344\270\255\346\267\273\345\212\240\351\237\263\344\271\220.md" @@ -0,0 +1,192 @@ +--- +layout: post + +title: "在博客中添加音乐" + +date: 2023-03-18 + +description: "介绍了如何在在博客中添加音乐的方法" + +tag: 个人网站 +--- +## 音乐-起风了 + +

https://link.jscdn.cn/sharepoint/aHR0cHM6Ly9oa3VzdGNvbm5lY3QtbXkuc2hhcmVwb2ludC5jb20vOnU6L2cvcGVyc29uYWwvamNoZW5mbF9jb25uZWN0X3VzdF9oay9FVE1tNEYwRU1CSkt0UGJKNDlmbkFVWUJWdUZXYUJ0RzVZTlVoRlFGV0FtY2NBP2U9RlZ4bkxB.mp3

+ +## 要求 + +* jekyll +* 七牛 +* Onedrive + +## 开始使用 + +step1 + +首先下载这个文件[open-embed.html](https://raw.githubusercontent.com/jhvanderschee/jekyllcodex/gh-pages/_includes/open-embed.html),右键,另存为 `Html`;下面是源码,也可以复制保存下面的源码; + +```javascript + + + +``` + +step2 + +将上一步存储好的文件,保存到**`_includes`**目录下,就像下面这个样子 + +![](https://img-blog.csdnimg.cn/20190910194004176.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3o1NjQzNTk4MDU=,size_16,color_FFFFFF,t_70) + +step3 + +在**`_layouts`**目录下,**`default.html`**这个文件,打开,在最下面,找到对应的标签 **``** ,加入如下代码,注意:标签不要重复了 + +![](https://img-blog.csdnimg.cn/20190910194101974.png) + +### 使用语法 + +借助七牛云之类的图床或云端存储,将需要的音乐文件上传至图床,并获得外链。 + +然后,在Markdown中直接使用: + +```cobol +

音乐外链url

+``` + +注意:音乐链接url增加了支持MP3、wma、m4a格式 + +### Onedrive音乐文件获取外链 + +参考如下链接: + +#### 生成外链 + +先想办法把你要生成外链的音乐或者图片上传到OneDrive中。 +网页版的应该是被墙了,不科学上网用不了。 +现在的电脑上一般都有OneDrive的,自己搜一下。 + +![在这里插入图片描述](https://img-blog.csdnimg.cn/2020052811121712.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NjY3MTcw,size_16,color_FFFFFF,t_70) +上图中我已经把两首歌放到OneDrive中了。 + +然后把文件共享:在文件上右击,点 `共享` + +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200528111412213.png) + +共享之后你就能看到这个,点左下角的按钮复制链接。 + +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200528111519697.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NjY3MTcw,size_16,color_FFFFFF,t_70) + +我复制到的链接如下: + +> https://1drv.ms/u/s!AjennNdWMahGijT1WWHNP-Wjj_CF?e=LNajCn + +现在这样子肯定不能直接用了,需要借助转换工具。 + +可以直接用这个生成链接: + +[https://onedrive.gimhoy.com/](https://onedrive.gimhoy.com/) + +你输入之后就会可以生成了。 + +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200528113307628.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NjY3MTcw,size_16,color_FFFFFF,t_70) + + **还有一个小工具可以批量生成** : + +[https://github.com/Mapaler/GetOneDriveDirectLink](https://github.com/Mapaler/GetOneDriveDirectLink) + +[https://mapaler.github.io/GetOneDriveDirectLink/](https://mapaler.github.io/GetOneDriveDirectLink/) diff --git "a/_posts/2023-03-22-\351\253\230\345\271\266\345\217\221Locust\351\253\230\345\271\266\345\217\221\346\216\245\345\217\243\346\265\213\350\257\225\346\241\206\346\236\266\344\273\213\347\273\215.md" "b/_posts/2023-03-22-\351\253\230\345\271\266\345\217\221Locust\351\253\230\345\271\266\345\217\221\346\216\245\345\217\243\346\265\213\350\257\225\346\241\206\346\236\266\344\273\213\347\273\215.md" new file mode 100644 index 0000000000..47bc6298b5 --- /dev/null +++ "b/_posts/2023-03-22-\351\253\230\345\271\266\345\217\221Locust\351\253\230\345\271\266\345\217\221\346\216\245\345\217\243\346\265\213\350\257\225\346\241\206\346\236\266\344\273\213\347\273\215.md" @@ -0,0 +1,110 @@ +--- +layout: post + +title: "高并发Locust高并发接口测试框架介绍" + +date: 2023-03-18 + +description: "介绍了高并发Locust高并发接口测试框架,进行性能测试,查看" + +tag: 自动化测试 +--- +## 谢春花的歌 + +

https://link.jscdn.cn/sharepoint/aHR0cHM6Ly9oa3VzdGNvbm5lY3QtbXkuc2hhcmVwb2ludC5jb20vOnU6L2cvcGVyc29uYWwvamNoZW5mbF9jb25uZWN0X3VzdF9oay9FVThfNm53Y1ZSQkVtc1FCdzY1WDJnNEJmcnhjaXBrSDdKZHZvWWg2R1Z0ekR3P2U9QUh6cWhP.mp3

+ +## 什么是[locust](https://so.csdn.net/so/search?q=locust&spm=1001.2101.3001.7020)? + +locust是一个简单易用的分布式[负载测试](https://so.csdn.net/so/search?q=%E8%B4%9F%E8%BD%BD%E6%B5%8B%E8%AF%95&spm=1001.2101.3001.7020)工具,主要用来对网站进行负载压力测试。locust使用python语言开发,测试资源消耗远远小于java语言开发的jmeter。且其支持分布式部署测试,能够轻松模拟百万级用户并发测试。 + + +## 什么是性能测试 + + +[性能测试](https://so.csdn.net/so/search?q=%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95&spm=1001.2101.3001.7020)目的主要是解决三个问题: + +* 系统及服务能承受的最大负载是多少 ? +* 有没有性能平瓶颈 ? +* 如果有性能瓶颈,瓶颈在哪里? + +这三个问题需要从测试的结果中分析得出,其中最重要的性能指标有三点: + +(1)响应时间 + +(2)吞吐量 + +(3)成功率 + + +传统的性能测试工具有很多, 比如 **Apache Bench、JMeter、LoadRunner **等等,它们都能用来持续增加压力。 + +不过,**Apache Bench** 之类的专用工具用来做简单的 HTTP 接口测试还行,如果需要做业务接口的串联测试就力有未逮了。 + +**JMeter **当然功能强大,也有一定的扩展性,但在这节课我并不想用 JMeter , 原因有两点,一是因为 JMeter 是资源消耗黑洞, 每个任务 / 用户都要使用一个线程,二是因为 JMeter 是基于配置的。 + +相比之下,新一代的性能测试工具 ** Locust** (蝗虫)是基于编程来实现测试用例的性能测试工具,它更加灵活。而且,它使用 Python 代码来定义用户行为,用写 Python 代码的方式来写测试脚本,远离了复杂的配置脚本和图形界面,利用强大而丰富的 Python 类库可以轻松支持各种协议,简单易用,扩展方便。 + +## locust优缺点 + +**优点:** + +1.不同与wrk和jmeter使用线程数提高并发量,locust借助于协程实现对用户的模拟,相同物理资源(机器cpu、内存等)配置下locust能支持的并发用户数相比jmeter可以提升一个数量级 +2.相比wrk对复杂场景测试的捉襟见肘和jmeter需要界面点击录制复杂场景的麻烦,locust只需用户使用python编写用户场景完成测试 +3.不同与jmeter复杂的用户使用界面,locust的界面干净整洁,可以实时显示测试的相关细节(如发送请求数、失败数和当前发送请求速度等) +4.locust虽然是面向web应用测试的,但是它可以用来测试几乎所有系统。给locust编写一个客户端,可以满足你所有的测试要求 + +**缺点:** + +1.同wrk一样,locust测试结果输出不如jmeter的测试结果展示类型多 + +## 运行 + +运行脚本如下: + +```python +from config.config_read import read_yml_file, read_test_data +from locust import HttpUser, task + +headers = read_yml_file('./config/config.yml')['headers'] +params = read_test_data('./config/test_data_process.yml', 'findByPidAndStatusActive')[0] + + + +class Quickstart(HttpUser): + min_wait = 100 # 最小等待时间(ms),模拟用户在执行每个任务之间等待的最小时间 + max_wait = 500 # 最大等待时长(ms),模拟用户在执行每个任务之间等待的最大时长 + host = read_yml_file('./config/config.yml')['baseurl'] # 访问的域名 + + def on_start(self): + #开始任务 + print("start working") + + # 任务target,用@task标记 + @task + def mytask(self): + self.client.get('/process/findByPidAndStatusActive', headers=headers, params=params) +``` + +1. 运行脚本方法: + +```shell + locust -f ***.py +``` + +-f 指定脚本文件路径 + +2. 访问locust UI界面 + +Starting web interface at http://0.0.0.0:8089 (accepting connections from all network interfaces) + +3. 模拟用户访问 + + 指定模拟的用户数(Number of users),以及孵化率(Spawn rate,每秒增加的用户数), 以及所要测试的服务器地址(Host)。 + ![20230322172524](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230322172524.png) +4. 查看结果 + RPS(Request Per Second): 每秒请求数的变化,它反映了吞吐量 + Number of Users, 用户并发数量的变化, 它反映了系统能承受的并发请求吞吐量 + Response Time 响应时间的变化 + + + diff --git "a/_posts/2023-03-23-linux\347\237\245\350\257\206\345\215\241\347\211\207\345\274\217\345\205\250\350\265\204\346\226\231\346\225\264\347\220\206.md" "b/_posts/2023-03-23-linux\347\237\245\350\257\206\345\215\241\347\211\207\345\274\217\345\205\250\350\265\204\346\226\231\346\225\264\347\220\206.md" new file mode 100644 index 0000000000..3a711ba80c --- /dev/null +++ "b/_posts/2023-03-23-linux\347\237\245\350\257\206\345\215\241\347\211\207\345\274\217\345\205\250\350\265\204\346\226\231\346\225\264\347\220\206.md" @@ -0,0 +1,40 @@ +--- +layout: post + +title: "linux知识卡片式全资料整理" + +date: 2023-03-23 + +description: "介绍了如何使用ChatGPT制作知识卡片并在Anki卡片中利用遗忘曲线对知识点进行周期性地复习" + +tag: +--- +# 1. 什么是知识卡片 + +# 2. 如何使用ChatGPT制作知识卡片 + +我希望你能作为一个专业的抽认卡制作者,能够根据我提供的文本制作抽认卡。 + +制作抽认卡的说明: + +- 一张抽认卡包含一个编号、问题、答案和其他细节 +- 保持抽认卡的简单、清晰,合理换行,并集中于最重要的信息。 +- 确保正确转义所有符号。 +- 确保问题是具体的、不含糊的。 +- 使用清晰和简洁的语言。使用简单而直接的语言,使卡片易于阅读和理解。 +- 答案应该只包含一个关键的事实/名称/概念/术语。 +- 关于答案的更多信息应始终放在"细节"一栏中。 +- 输入文本是excel的文本。 + +请将你制作的卡片以markdown表格(问题/答案/细节)的形式输出,不要有任何额外的文字。 + +如果你明白,请回复:"我已经学会了如何创建抽认卡。请为我提供文本"。 + +# 3. 如何在Anki卡片中利用遗忘曲线对知识点进行周期性地复习 + +# 4. Linux知识点卡片 + +# 5. 参考文献 + +ChatGPT 出现后,那些靠知识记忆取胜的教育模式会被颠覆吗? - 叶峻峣的回答 - 知乎 +https://www.zhihu.com/question/583015287/answer/2901234936 diff --git "a/_posts/2023-03-24-Linux\347\273\274\345\220\210\346\243\200\346\265\213\346\226\271\346\241\210\350\256\276\350\256\241.md" "b/_posts/2023-03-24-Linux\347\273\274\345\220\210\346\243\200\346\265\213\346\226\271\346\241\210\350\256\276\350\256\241.md" new file mode 100644 index 0000000000..539c57d485 --- /dev/null +++ "b/_posts/2023-03-24-Linux\347\273\274\345\220\210\346\243\200\346\265\213\346\226\271\346\241\210\350\256\276\350\256\241.md" @@ -0,0 +1,365 @@ +--- +layout: post +title: Linux综合检测方案 +date: 2023-03-24 +description: "介绍了Linux使用初期如何使用命令查看系统信息;以及如何使用定时任务查看系统运行稳定性" +tag: Linux +--- +# 1. 基本命令 + +top + +du + +## 查看/proc目录下的系统各种信息 + +/proc 是一个虚拟文件系统,它提供了一种机制,通过读取和写入特定的文件或目录,可以访问内核和系统信息。 + +```shell +# 命令行数太多可以使用more命令或者grep命令进行筛选 +# 查看CPU信息 +cat /proc/cpuinfo +# 会输出包含 "model name" 字符串的行,即 CPU 型号的信息 +cat /proc/cpuinfo | grep "model name" +# 只查看 CPU 核心数,可以使用以下命令: +cat /proc/cpuinfo | grep "cpu cores" + +# 查看内存参数 +cat /proc/meminfo +# 查看总内存 +cat /proc/meminfo | grep "MemTotal" +# 查看系统空闲内存大小 +cat /proc/meminfo | grep "MemFree" + +# 系统的各种参数和设置,如内核参数、网络设置、文件系统设置等 +# abi debug dev fs kernel net user vm +cat /proc/sys/* +# 网络信息,如 TCP/IP 连接、网络接口、路由表等 +# 包括tcp、tcp6、udp、udp6等 +cat /proc/net/* +# 查看tcp连接的各种信息 +cat /proc/net/tcp + +# 指定进程的信息,如进程状态、内存占用、打开的文件等(进程资源使用情况) +cd /proc/pidnumber +``` + +## top:查看系统的进程信息,包括 CPU 占用率、内存占用率等。 + +重要参数: + +-d: seconds:指定 top 命令的刷新时间间隔,单位为秒;如,"top -d 5" 表示每 5 秒刷新一次 top 命令的输出; + +-b:以批处理模式运行 top 命令,将结果输出到文件中,而不是交互式界面; + +-p pid1,pid2,...:只显示指定进程 ID 的进程信息,多个进程 ID 之间用逗号分隔。 + +-n iterations:指定 top 命令的迭代次数,即 top 命令将输出多少次后自动退出。例如,"top -n 10" 表示 top 命令将输出 10 次后自动退出; + +## htop命令 + +显示系统中运行进程的状态和资源使用情况,界面更加美观。 + +## atop:都是用于查看系统进程信息的命令,可以按照不同的方式显示进程信息。 + +## vmstat:可以监控操作系统的进程状态、内存、虚拟内存、磁盘IO、CPU的信息。 + +-a:显示活动和非活动的内存使用情况; + +-d:显示磁盘 I/O 统计信息 + +1. iostat:查看系统的磁盘 I/O 使用情况。 +2. netstat:查看系统的网络连接情况。 + +输出接果: + +Proto:显示网络连接的协议类型,如 TCP、UDP 等。 + +Local Address 显示本地IP地址和端口号 + +Recv-Q:显示接收队列的大小 + +## nmon:可以监测 CPU 使用率、内存使用情况、磁盘 I/O、网络流量、进程和线程等多个系统资源,还可以监测系统温度和风扇转速等硬件指标。 + +## dmesg:来显示内核环缓冲区(kernel-ring buffer)内容。 + +1. free:查看系统的内存使用情况,包括物理内存、交换内存(swap)和内核缓冲区内存。 + + 输出: + + **buff/cache** 列显示被 buffer 和 cache 使用的物理内存大小。 + +## df disk free + +显示磁盘分区上可以使用的磁盘空间;(通过文件系统来快速获取空间大小的信息,当我们删除一个文件的时候,这个文件不是马上就在文件系统当中消失了,而是暂时消失了,当所有程序都不用时,才会根据OS的规则释放掉已经删除的文件, df记录的是通过文件系统获取到的文件的大小,他比du强的地方就是能够看到已经删除的文件,而且计算大小的时候,把这一部分的空间也加上了,更精确了。当文件系统也确定删除了该文件后,这时候du与df就一致了。) +-a #查看全部文件系统,单位默认KB +-h #使用-h选项以KB、MB、GB的单位来显示,可读性高 + +## du disk usage + +显示每个文件和目录的磁盘使用空间,也就是文件的大小。(通过搜索文件来计算每个文件的大小然后累加,du能看到的文件只是一些当前存在的,没有被删除的。他计算的大小就是当前他认为存在的所有文件大小的累加和。) + +-h : 以K M G为单位显示,提高可读性 + +--max-depth=1 : 显示层级 + +## uptime:查看系统的运行时间和平均负载。 + +load average: 3.17, 3.45, 3.65,分别描述了1分钟5分钟15分钟内系统平均负载. + +## ps:查看系统的进程信息,包括进程 ID、CPU 占用率等。 + +-aux :显示所有进程,包括其他用户的进程。 + +aux格式:USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND + +USER: 行程拥有者 +PID: pid +%CPU: 占用的 CPU 使用率 +%MEM: 占用的记忆体使用率 +VSZ: 占用的虚拟记忆体大小 +RSS: 占用的记忆体大小 +TTY: 终端的次要装置号码 (minor device number of tty) +STAT: 行程状态 + +查找指定进程(如Java)的进程号: +ps -ef | grep java + +## lsof:查看系统打开的文件和网络连接情况;lscpu:查看CPU信息. + +## tcpdump:抓取和分析网络数据包。 + +## ping、traceroute、mtr:用于测试网络连接和延迟。 + +ping -c 5 www.baidu.com # -c 5表示发送5个数据包 + +traceroute命令用于追踪数据包在网络上的传输时的全部路径,它默认发送的数据包大小是40字节。通过traceroute我们可以知道信息从你的计算机到互联网另一端的主机是走的什么路径. +traceroute www.baidu.com + +## rsync:用于远程备份和同步文件和目录。 + +同步同一台机上的两个目录: + +```shell +$ rsync -zvr /var/opt/installation/inventory/ /root/temp +building file list ... done +sva.xml +svB.xml +. +sent 26385 bytes received 1098 bytes 54966.00 bytes/sec +total size is 44867 speedup is 1.63 +``` + +-z --compress:压缩数据 +-v --verbose:显示详细信息 +-r --recursive:递归处理,将指定目录下的所有文件和子目录一并处理 + +还可以拷贝单个文件: + +```shell +$ rsync -v /var/lib/rpm/Pubkeys /root/temp/ +Pubkeys + +sent 42 bytes received 12380 bytes 3549.14 bytes/sec +total size is 12288 speedup is 0.99 +``` + +从本地拷贝多个文件到远端: + +```shell +$ rsync -avz /root/temp/ thegeekstuff@192.168.200.10:/home/thegeekstuff/temp/ +Password: +building file list ... done +./ +rpm/ +rpm/Basenames +rpm/Conflictname + +sent 15810261 bytes received 412 bytes 2432411.23 bytes/sec +total size is 45305958 speedup is 2.87 +``` + +从本地拷贝多个文件到远端: +使用rsync, 也可以从本地拷贝多个文件或目录到远端. + +```shell +$ rsync -avz /root/temp/ thegeekstuff@192.168.200.10:/home/thegeekstuff/temp/ +Password: +building file list ... done +./ +rpm/ +rpm/Basenames +rpm/Conflictname + +sent 15810261 bytes received 412 bytes 2432411.23 bytes/sec +total size is 45305958 speedup is 2.87 +``` + +从远程服务器拷贝文件到本地: + +```shell +$ rsync -avz thegeekstuff@192.168.200.10:/var/lib/rpm /root/temp +Password: +receiving file list ... done +rpm/ +rpm/Basenames +. +sent 406 bytes received 15810230 bytes 2432405.54 bytes/sec +total size is 45305958 speedup is 2.87 +``` + +## scp、sftp:用于远程复制和传输文件。 + +scp是一种基于SSH的协议,可在网络上的主机之间提供文件传输。 + +## ssh、telnet:用于远程登录和管理系统。 + +telnet是明文传送, ssh是加密的且支持压缩。 + +## crontab:用于定时执行任务。 + +## systemctl、service:用于管理系统服务和启动、停止、重启服务。 + +## firewall-cmd、iptables:用于管理系统防火墙和配置网络规则。 + +systemctl start firewalld # 启动防火墙 +systemctl stop firewalld # 关闭防火墙 +systemctl restart firewalld # 重启防火墙 +systemctl status firewalld # 查看防火墙状态 +systemctl enable firewalld # 设置开机启动 + +## selinux:用于管理系统安全策略和访问控制。 + +## sysctl:用于调整内核参数和系统性能优化。 + +sysctl命令被用于在内核运行时动态地修改内核的运行参数,可用的内核参数在目录/proc/sys中。它包含一些TCP/IP堆栈和虚拟内存系统的高级选项, 这可以让有经验的管理员提高引人注目的系统性能。用sysctl可以读取设置超过五百个系统变量。 + +```shell +vim /etc/sysctl.conf +net.ipv4.ip_forward = 1 # 开启IP转发 +net.ipv4.conf.all.accept_redirects = 0 # 禁止所有接口接收重定向报文 +``` + +## ulimit:用于限制系统资源使用和保护系统安全。 + +## perf、、systemtap:用于系统性能分析和瓶颈诊断。 + +## perf、、systemtap:用于系统性能分析和瓶颈诊断。 + +## init、systemd、upstart:用于系统进程管理和服务管理。 + +## lspci、lsusb、lshw:用于硬件信息和诊断。 + +## lp、lpr、cups:用于打印管理和打印队列控制。 + +## tree、fd、rg:用于文件和目录搜索和查找。 + +# 2. 综合检测集成方案和工具 + +## Unixbench + +Unixbench是一个类unix系(如Unix, Linux)统下的性能测试工具,被广泛用在linux系统主机的性能的测试,主要提供的测试项目有:系统调用、读写、进程、图形化测试、2D、3D、管道、运算、C库等系统基准性能提供测试数据。 + +要运行UnixBench测试,请按照以下步骤进行: + +打开终端并进入UnixBench的根目录。在大多数情况下,UnixBench的根目录位于/usr/share/UnixBench。您可以使用以下命令进入该目录: + +1. cd /usr/share/UnixBench + 如果UnixBench安装在其他位置,请相应地更改路径。 +2. 运行./Run命令。这将会运行所有可用的测试,包括CPU、内存、磁盘、文件系统和网络等方面的测试。测试过程可能需要一些时间,具体时间取决于您的系统性能。 +3. 等待测试完成后,将生成一个HTML格式的报告。该报告位于UnixBench根目录下的results文件夹中,文件名为UnixBench.html。您可以使用Web浏览器打开该文件,并查看测试的结果和性能数据。 + +除了运行所有测试之外,您还可以运行单个测试或一组测试。以下是一些示例命令: + +运行CPU测试:./Run cpu +运行内存测试:./Run memory +运行磁盘测试:./Run disk +运行文件系统测试:./Run fs +运行网络测试:./Run network + +## Sysbench工具 + +内建测试项 + +fileio:文件I/O测试。 +cpu:CPU性能测试。 +memory:内存速度测试。 +threads:线程子系统性能测试。 +mutex:互斥性能测试。 +oltp:OLTP(在线事务处理)测试。 + +## 其他工具 + +Nagios:一款开源的企业级 IT 基础设施监控软件,可监测服务器、网络设备、应用程序等多种设备和服务状态。 + +Zabbix:一款功能强大的开源网络监控软件,支持多种监测方式,如 SNMP、JMX、IPMI、HTTP 等,并具有自定义告警、数据可视化等功能。 + +Munin:一款基于 Web 的监测软件,可以监测 CPU、内存、磁盘、网络等多种系统指标,还支持插件扩展和数据可视化。 + +Cacti:一款基于 RRDtool 的网络图形化监测工具,可以监测网络带宽、路由器、交换机等设备的状态,并生成图表和报告。 + +Prometheus:一款开源的服务监测系统,可监测多种应用程序、数据库、中间件等服务状态,并提供数据可视化和告警功能。 + +Sysdig:一款基于系统调用跟踪的容器监测工具,可以监测容器的运行状态、资源使用情况、网络流量等,并提供数据可视化和安全分析功能。 + +Netdata:一款轻量级的实时系统监测工具,支持多种系统指标和网络流量监测,并具有数据可视化和告警功能。 + +## 网络监视器,如 tcpdump、Wireshark + +# 3. 检测步骤 + +检查系统资源使用情况:可以使用 top 命令查看系统中正在运行的进程,以及它们占用的 CPU 和内存资源。可以使用 free 命令查看系统中可用的内存和交换空间的使用情况。 + +检查磁盘使用情况:可以使用 df 命令查看系统中磁盘分区的使用情况,以及剩余的可用空间。可以使用 du 命令查看特定目录或文件的磁盘使用情况。 + +检查网络带宽和连接情况:可以使用 iftop 命令查看系统中正在使用的网络连接和带宽使用情况。可以使用 netstat 命令查看特定端口或协议的网络连接情况。 + +检查系统负载和性能指标:可以使用 uptime 命令查看系统的负载情况。可以使用 vmstat 命令查看系统的虚拟内存使用情况和 CPU 使用情况。可以使用 iostat 命令查看系统的磁盘 I/O 使用情况。 + +进行基准测试:可以使用一些基准测试工具,如 UnixBench、Geekbench、Sysbench 等,对系统进行性能测试,以便比较不同系统或不同配置的性能差异。 + +# 4. 使用crontab定时调度任务进行自动化运维(对系统日志进行备份等) + +有时候,为了系统稳定性,除了在/var/log通过回滚机制生成各种日志文件以排查系统故障之外,我们也想把这些关键的日志文件保存到指定文件夹,防止突然断电或者磁盘分区被破坏之后丢失关键的日志信息。 + +代码如下:(文件名为backup_sysconfig.sh) + +```shell +#! /bin/bash +# this script is used to backup the syslog file in /var/log dir everyday +echo `date` +cp /var/log/syslog /home/hugo/test_BASH/log/ +# adding the user with x permission +chmod u+x /home/hugo/test_BASH/log/syslog +# chnage the firname +today=$(date +%y%m%d) +echo $today +mv /home/hugo/test_BASH/log/syslog /home/hugo/test_BASH/log/syslog_$today +``` + +## 定时调度任务 + +crontab 的语句是“分 时 日 月 周 命令”. + +开启定时调度任务,每天凌晨1点执行一次 + +```shell +crontab -e +# 0 1 * * * /home/hugo/test_BASH/backup_sysconfig.sh +``` + +显示 `crontab: installing new crontab`,表示定时任务已经开启。 + +查看定时任务 + +```shell +crontab -l +``` + +开启或者关闭定时任务 + +```shell +service crondstart +service crondstop + +``` diff --git "a/_posts/2023-03-26-\344\275\277\347\224\250cursor\350\207\252\345\212\250\345\214\226\347\224\237\346\210\220\344\273\243\347\240\201\347\273\230\345\210\266\345\271\277\344\270\234\347\234\201\345\234\260\345\233\276.md" "b/_posts/2023-03-26-\344\275\277\347\224\250cursor\350\207\252\345\212\250\345\214\226\347\224\237\346\210\220\344\273\243\347\240\201\347\273\230\345\210\266\345\271\277\344\270\234\347\234\201\345\234\260\345\233\276.md" new file mode 100644 index 0000000000..9e03157de9 --- /dev/null +++ "b/_posts/2023-03-26-\344\275\277\347\224\250cursor\350\207\252\345\212\250\345\214\226\347\224\237\346\210\220\344\273\243\347\240\201\347\273\230\345\210\266\345\271\277\344\270\234\347\234\201\345\234\260\345\233\276.md" @@ -0,0 +1,149 @@ +--- +layout: post +title: 使用cursor自动化生成代码绘制广东省地图 +date: 2023-03-26 +description: "介绍了如何使用cursor自动化生成代码绘制广东省地图" +tag: 物理海洋学 +--- +## 准备工作 + +shp文件是一个矢量文件,可以用来绘制地图,包括guangdong.dbf(属性表)、guangdong.shp(地图信息)、guangdong.shx(索引信息)。 + +## cursor是什么? + +Cursor.so 是一个集成了 GPT-4 的国内直接可以访问的,优秀而强大的免费代码生成器,可以帮助你快速编写、编辑和讨论代码。 + 它支持多种编程语言,如 Python, Java, C#, JavaScript 等,并且可以根据你的输入和需求自动生成代码片段。 + Cursor.so 还可以帮助你重构、理解和优化代码,提高开发效率。 + +官网地址:[https://www.cursor.so/](https://www.cursor.so/) + +## 代码生成 + +### 使用geopandas + +```python +import geopandas as gpd +import matplotlib.pyplot as plt + +# Read the shapefile +gdf = gpd.read_file('guangdong.shp', encoding='utf-8') + +# Plot the map +gdf.plot() + +# Show the plot +plt.show() +``` + +结果如下: + +![20230326135111](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230326135111.png) + +### folium和geopandas + +folium 建立在 Python 生态系统的数据应用能力和 Leaflet.js 库的映射能力之上,在Python中操作数据,然后通过 folium 在 Leaflet 地图中可视化。 + +```python +import geopandas as gpd +import folium + +# Read the shapefile +gdf = gpd.read_file('guangdong.shp', encoding='utf-8') + +# Set the CRS of the GeoDataFrame +gdf = gdf.set_crs('EPSG:4326') + +# Create a new map +m = folium.Map(location=[23.5, 116.5], zoom_start=7) + +# Add the shapefile to the map +folium.GeoJson(gdf).add_to(m) + +# Save the map as an HTML file +m.save('map.html') +``` + +结果如下: + +![20230326135152](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230326135152.png) + +### 使用shapefile + +```python +import shapefile +import matplotlib.pyplot as plt + +# Read the shapefile +sf = shapefile.Reader("guangdong.shp") + +# Plot the map +for shape in sf.shapeRecords(): + x = [i[0] for i in shape.shape.points[:]] + y = [i[1] for i in shape.shape.points[:]] + plt.plot(x, y) + +# Show the plot +plt.show() + +# %% +import shapefile +import matplotlib.pyplot as plt + +# Read the shapefile +sf = shapefile.Reader("guangdong.shp") +``` + +### shapefile2 + +``` +import shapefile +import matplotlib.pyplot as plt + +# Read the shapefile +sf = shapefile.Reader("guangdong.shp") + +# Plot the map +for shape in sf.shapeRecords(): + x, y = zip(*shape.shape.points) + plt.plot(x, y) + +# Show the plot +plt.show() +``` + +结果如下: + +![20230326135239](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230326135239.png) + +### 使用basemap + + +```python +from mpl_toolkits.basemap import Basemap +import shapefile +import matplotlib.pyplot as plt + +# Read the shapefile +sf = shapefile.Reader("guangdong.shp") + +# Create a new map +m = Basemap(llcrnrlon=109, llcrnrlat=20, urcrnrlon=118, urcrnrlat=26, projection='lcc', lat_1=33, lat_2=45, lon_0=100) + +# Plot the map +for shape in sf.shapeRecords(): + x, y = zip(*shape.shape.points) + x, y = m(x, y) + m.plot(x, y, 'k-', linewidth=0.5) + +# Show the plot +plt.show() +``` + +结果如下: + +![20230326135308](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230326135308.png) + + +## 项目地址 + +[guangdong_map](https://github.com/ChanJeunlam/guangdong_map) \ No newline at end of file diff --git "a/_posts/2023-03-28-Linux\347\273\274\345\220\210\346\243\200\346\265\213\346\226\271\346\241\210\350\276\223\345\207\272\350\207\252\345\212\250\345\214\226\346\212\245\345\221\212.md" "b/_posts/2023-03-28-Linux\347\273\274\345\220\210\346\243\200\346\265\213\346\226\271\346\241\210\350\276\223\345\207\272\350\207\252\345\212\250\345\214\226\346\212\245\345\221\212.md" new file mode 100644 index 0000000000..905bc66a53 --- /dev/null +++ "b/_posts/2023-03-28-Linux\347\273\274\345\220\210\346\243\200\346\265\213\346\226\271\346\241\210\350\276\223\345\207\272\350\207\252\345\212\250\345\214\226\346\212\245\345\221\212.md" @@ -0,0 +1,106 @@ +--- +layout: post +title: Linux综合检测方案输出自动化报告 +date: 2023-03-28 +description: "使用SHELL脚本编程,对Linux进行综合检测并将检测方案输出自动化报告" +tag: Linux +--- + + + +## 代码 + +```shell +# 添加当前时间信息 +echo "当前时间为$(date)" >> output.txt + +# 会输出包含 "model name" 字符串的行,即 CPU 型号的信息 +model_name=$(cat /proc/cpuinfo | grep "model name" | head -n 1 | cut -d ":" -f 2 | xargs) +echo "CPU型号信息为$model_name" >> output.txt +# 查看内核核心数量 +cpucores=$(cat /proc/cpuinfo | grep "cpu cores" | head -n 1 | cut -d ":" -f 2 | xargs) +echo "内核核心数量为$cpucores" >> output.txt +# 查看系统 CPU 使用情况 +echo "系统 CPU 使用情况如下:" >> output.txt +iostat -c | awk '{if(NR==4){print "CPU繁忙度: "$1"%"} }' >> output.txt +iowait=$(iostat -c | awk '{if(NR==4){print $4} }') +if [ $(echo "$iowait >=60" | bc -l) -eq 1 ]; +then + echo "CPU等待输入输出完成时间的百分比为$iowait%,过高,表示硬盘存在I/O瓶颈。" >> output.txt +else + echo "CPU等待输入输出完成时间的百分比为$iowait%,正常。" >> output.txt +fi +# 如果%idle值高,表示CPU较空闲 +# 如果%idle值持续低于10,表明CPU处理能力相对较低,系统中最需要解决的资源是CPU。 +idle=$(iostat -c | awk '{if(NR==4){print $6} }') +if [ $(echo "$idle >= 90" | bc -l) -eq 1 ]; +then + echo "CPU较空闲,%idle值为$idle" >> output.txt +elif [ $(echo "$idle < 10" | bc -l) -eq 1 ]; +then + echo "CPU处理能力相对较低,idle值为$idle%" >> output.txt +fi + + +# 输出系统总内存 +MemTotal=$(cat /proc/meminfo | grep "MemTotal" | cut -d ":" -f 2 | xargs) +echo "系统总内存为$MemTotal" >> output.txt +# 查看系统空闲内存大小 +MemFree=$(cat /proc/meminfo | grep "MemFree" | cut -d ":" -f 2 | xargs) +echo "系统空闲内存为$MemFree" >> output.txt +# Cached:分配给文件缓冲区的内存(比如未保存的文件就是写到该缓冲区) +Cached=$(cat /proc/meminfo | grep "Cached" | head -n 1 | cut -d ":" -f 2 | xargs) +echo "分配给文件缓冲区的内存(比如未保存的文件就是写到该缓冲区)为$Cached。" >> output.txt + +# 查看磁盘信息 +echo "磁盘容量信息如下:" >> output.txt +df -h | sort -k 4 -hr | grep "文件系统" >> output.txt +df -h | sort -k 4 -hr | grep -v "文件系统" | grep -v "tmpfs" >> output.txt + +# 查看网络 +PingInfo=$(ping -c 5 www.baidu.com | grep "bytes from" | head -n 1 ) +echo "通过ping访问百度得到的信息如下:\n $PingInfo">> output.txt +PingStatics=$(ping -c 5 www.baidu.com | grep "1 packets transmitted" | head -n 1 ) +echo "$PingStatics">> output.txt + +rtt=$(ping -c 5 www.baidu.com | grep "rtt" | cut -d "=" -f 2 | head -n 1 ) +rttmin=$(echo $rtt | cut -d "/" -f 1) +rttavg=$(echo $rtt | cut -d "/" -f 2) +rttmax=$(echo $rtt | cut -d "/" -f 3) +rttmdev=$(echo $rtt | cut -d "/" -f 4) +echo "网络最小时延为$rttmin ms" >> output.txt +echo "网络平均时延为$rttavg ms" >> output.txt +echo "网络最大时延为$rttmax ms" >> output.txt +echo "网络时延平均偏差为$rttmdev" >> output.txt + +# perf性能瓶颈分析,查看哪些进程占用CPU较高 +# 安装命令 +# apt-get install linux-tools-$(uname -r) linux-tools-generic -y +# https://blog.51cto.com/liuzhengwei521/2360430 +#echo "CPU性能瓶颈分析如下:" >> output.txt +#perf top -b -p $(pidof nginx) >> output.txt + + +# 查看系统负载 +# https://blog.51cto.com/ghostwritten/5344990 +# 对CPU进行压力测试 +#cpu_number=$(grep -c 'processor' /proc/cpuinfo) +#创建8个stress进程和100个io进程,持续时间600秒,模拟CPU在用户态和内核态总使用率达到100%的场景。 +#stress -c $cpu_number -i 100 --verbose --timeout 600 + +# 对内存进行压力测试 +# 下面例子,就是创建5个进程,同时去malloc分配内存,并且保持100s后再释放内存: +# stress --vm 5 --vm-bytes 1G --vm-hang 100 --timeout 100s +# stress 测试内存的时候,--vm-bytes 1G --vm-hang 100 这里2个参数是关键!!!! +# --vm-bytes 表示malloc分配多少内存 +# --vm-hang 表示malloc分配的内存多少时间后在free()释放掉 +# --vm 指定进程数量,分配的内存总数为vm*vm-bytes +# --timeout 持续时间 + +# 对磁盘进行压力测试,并输出结果到文件 +# stress --hdd 2 --hdd-bytes 3G --timeout 200 +# -hdd forks 产生多个执行write()函数的进程 +# --hdd-bytes bytes 指定写的Bytes数,默认是1GB +``` + +## 结果 diff --git "a/_posts/2023-03-29-Docker\345\205\245\351\227\250\345\237\272\347\241\200\347\237\245\350\257\206.md" "b/_posts/2023-03-29-Docker\345\205\245\351\227\250\345\237\272\347\241\200\347\237\245\350\257\206.md" new file mode 100644 index 0000000000..edaf919ceb --- /dev/null +++ "b/_posts/2023-03-29-Docker\345\205\245\351\227\250\345\237\272\347\241\200\347\237\245\350\257\206.md" @@ -0,0 +1,496 @@ +--- +layout: post +title: Docker入门基础知识 +date: 2023-03-29 +description: "介绍了Docker入门基础知识" +tag: Docker +--- +### Docker相关的概念 + +#### 1.1 虚拟机和容器 + +借助于VMWare等软件,可以在一台计算机上创建多个虚拟机,每个虚拟机都拥有独立的操作系统,可以各自独立的运行程序。这种分身术虽然隔离度高(操作系统级),使用方便(类似物理机),但占用存储资源多(GB级)、启动速度慢(分钟级)的缺点也是显而易见的。 + +相较于虚拟机,容器(Container)是一种轻量型的虚拟化技术,它虚拟的是最简运行环境(类似于沙盒)而非操作系统,启动速度快(秒级)、占用存储资源少(KB级或MB级),容器间隔离度为进程级。在一台计算机上可以运行上千个容器,这是容器技术对虚拟机的碾压式优势。 + +#### 1.2 容器、镜像和Docker + +Docker是一个开源的应用容器引擎,可以创建容器以及基于容器运行的程序。Docker可以让开发者打包他们的应用和依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。在Docker和容器之间,还隐藏着一个镜像的概念。本质上,Docker镜像是一个特殊的文件系统,它提供容器运行时所需的程序、库、资源、配置等文件。Docker镜像类似于一个py文件,它需要Docker的运行时(类似于Python解释器)运行。镜像被运行时,即创建了一个镜像的实例,一个实例就是一个容器。 + +镜像 +是一个构建容器的只读模板,是一个包含了应用程序和其运行时依赖环境的只读文件。它包含了容器启动所需的所有信息,包括运行程序和配置数据。每一个镜像由一系列的层 (layers) 组成。Docker 使用 UnionFS 来将这些层联合到单独的镜像中。UnionFS 允许独立文件系统中的文件和文件夹(称之为分支)被透明覆盖,形成一个单独连贯的文件系统。正因为有了这些层的存在,Docker 是如此的轻量。当你改变了一个 Docker 镜像,比如升级到某个程序到新的版本,一个新的层会被创建。因此,不用替换整个原先的镜像或者重新建立(在使用虚拟机的时候你可能会这么做),只是一个新的层被添加或升级了。现在你不用重新发布整个镜像,只需要升级,层使得分发 Docker 镜像变得简单和快速。 + +容器 +容器是从镜像创建的运行实例。它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。Docker 利用容器来运行应用。一个 Docker 容器都是独立和安全的应用平台,Docker 容器是 Docker 的运行部分。可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。 + +注: 镜像是只读的,容器在启动的时候创建一层可写层作为最上层。 + +![20230329151449](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230329151449.png) + +运行容器实例有两种方式: + +交互型容器: 前台运行,可以通过控制台与容器交互。如果创建该容器的终端被关闭,则容器就变为停止状态。此外,在容器控制台中输入exit或者通过docker stop或docker kill也能终止容器。 +后台型容器: 后台运行,创建启动之后就与终端无关了,需要用 docker stop 或 docker kill 来终止,或者 docker logs 查看运行中的输出。 +仓库 +Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。同样的,Docker 仓库也有公有和私有的概念。公有的 Docker 仓库名字是 Docker Hub。Docker Hub 提供了庞大的镜像集合供使用。这些镜像可以是自己创建,或者在别人的镜像基础上创建。Docker 仓库是 Docker 的分发部分。 + +Docker 仓库的概念与 Git 版本控制工具有些类似,注册服务器可以理解为 GitHub 这样的托管服务。 + +### Docker的安装 + +#### 2.1 在ubuntu中安装 + +在linux系统中安装Docker非常简单,官方为我们提供了一键安装脚本。这个方法也适用于Debian或CentOS等发行版。 + +``` +curl -sSL https://get.daocloud.io/docker | sh +``` + +安装过程如果出现超时,不要灰心,多试几次,总会成功的。安装完成后,Docker只能被root用户使用,可以使用下面的命令取消权限限制: + +``` +sudo gpasswd -a <你的用户名> docker +``` + +然后,重启docker服务: + +``` +sudo service docker restart +``` + +最后,关闭当前的命令行,重新打开新的命令行就可以了。 + +### Docker镜像的使用 + +docker官方的镜像库比较慢,在进行镜像操作之前,需要将镜像源设置为国内的站点。 + +新建文件/etc/docker/daemon.json,输入如下内容: + +``` +{ +    "registry-mirrors" : [ +        "https://registry.docker-cn.com", +        "https://docker.mirrors.ustc.edu.cn", +        "http://hub-mirror.c.163.com", +        "https://cr.console.aliyun.com/" +    ] +} +``` + +然后重启docker的服务: + +``` +systemctl restart docker +``` + +#### 3.1 列出本地所有镜像 + +执行命令 docker images 可以查看 + +``` +$ docker images +REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE +ubuntu              20.04               f643c72bc252        5 weeks ago         72.9MB +hello-world         latest              bf756fb1ae65        12 months ago       13.3kB +``` + +当前我本地只有刚才安装的两个镜像。 + +#### 3.2 从镜像库中查找镜像 + +执行命令 docker search 镜像名称可以从docker镜像库中查找镜像。 + +``` +$ docker search python +NAME                             DESCRIPTION                                     STARS              OFFICIAL          AUTOMATED +python                           Python is an interpreted, interactive, objec…   5757                [OK]                 +django                           Django is a free web application framework, …   1039                [OK]                 +pypy                             PyPy is a fast, compliant alternative implem…   260                 [OK]                 +joyzoursky/python-chromedriver   Python with Chromedriver, for running automa…   57                                      [OK] +nikolaik/python-nodejs           Python with Node.js                             57                                      [OK] +arm32v7/python                   Python is an interpreted, interactive, objec…   53                                       +circleci/python                  Python is an interpreted, interactive, objec…   42                                       +centos/python-35-centos7         Platform for building and running Python 3.5…   38                                       +centos/python-36-centos7         Platform for building and running Python 3.6…   30                                       +hylang                           Hy is a Lisp dialect that translates express…   29                  [OK]                 +arm64v8/python                   Python is an interpreted, interactive, objec…   24                                       +revolutionsystems/python         Optimized Python Images                         18                                       +centos/python-27-centos7         Platform for building and running Python 2.7…   17                                       +bitnami/python                   Bitnami Python Docker Image                     10                                      [OK] +publicisworldwide/python-conda   Basic Python environments with Conda.           6                                       [OK] +d3fk/python_in_bottle            Simple python:alpine completed by Bottle+Req…   5                                       [OK] +dockershelf/python               Repository for docker images of Python. Test…   5                                       [OK] +clearlinux/python                Python programming interpreted language with…   4                                        +i386/python                      Python is an interpreted, interactive, objec…   3                                        +ppc64le/python                   Python is an interpreted, interactive, objec…   2                                        +centos/python-34-centos7         Platform for building and running Python 3.4…   2                                        +amd64/python                     Python is an interpreted, interactive, objec…   1                                        +ccitest/python                   CircleCI test images for Python                 0                                       [OK] +s390x/python                     Python is an interpreted, interactive, objec…   0                                        +saagie/python                    Repo for python jobs                            0 +``` + +最好选择官方(OFFICIAL)的镜像,这样的镜像最稳定一些。 + +#### 3.3 下载新的镜像 + +执行命令docker pull 镜像名称:版本号即可下载新的镜像。 + +``` +$ docker pull python:3.8 +3.8: Pulling from library/python +6c33745f49b4: Pull complete  +ef072fc32a84: Pull complete  +c0afb8e68e0b: Pull complete  +d599c07d28e6: Pull complete  +f2ecc74db11a: Pull complete  +26856d31ce86: Pull complete  +2cd68d824f12: Pull complete  +7ea1535f18c3: Pull complete  +2bef93d9a76e: Pull complete  +Digest: sha256:9079aa8582543494225d2b3a28fce526d9a6b06eb06ce2bac3eeee592fcfc49e +Status: Downloaded newer image for python:3.8 +docker.io/library/python:3.8 +``` + +镜像下载后,就可以使用镜像来创建容器了。 + +### Docker容器的使用 + +#### 4.1 启动容器 + +执行命令docker run即可启动容器,也就是创建某个镜像的实例。docker run命令非常复杂,可以先执行一个docker run --help来查看帮助: + +``` +$ docker run --help + +Usage:  docker run [OPTIONS] IMAGE [COMMAND] [ARG...] + +Run a command in a new container + +Options: +      --add-host list                  Add a custom host-to-IP mapping (host:ip) +  -a, --attach list                    Attach to STDIN, STDOUT or STDERR +      --blkio-weight uint16            Block IO (relative weight), between 10 and 1000, or 0 to disable (default 0) +      --blkio-weight-device list       Block IO weight (relative device weight) (default []) +      --cap-add list                   Add Linux capabilities +      --cap-drop list                  Drop Linux capabilities +      --cgroup-parent string           Optional parent cgroup for the container +      --cidfile string                 Write the container ID to the file +      --cpu-period int                 Limit CPU CFS (Completely Fair Scheduler) period +      --cpu-quota int                  Limit CPU CFS (Completely Fair Scheduler) quota +      --cpu-rt-period int              Limit CPU real-time period in microseconds +      --cpu-rt-runtime int             Limit CPU real-time runtime in microseconds +  -c, --cpu-shares int                 CPU shares (relative weight) +      --cpus decimal                   Number of CPUs +      --cpuset-cpus string             CPUs in which to allow execution (0-3, 0,1) +      --cpuset-mems string             MEMs in which to allow execution (0-3, 0,1) +  -d, --detach                         Run container in background and print container ID +      --detach-keys string             Override the key sequence for detaching a container +      --device list                    Add a host device to the container +      --device-cgroup-rule list        Add a rule to the cgroup allowed devices list +      --device-read-bps list           Limit read rate (bytes per second) from a device (default []) +      --device-read-iops list          Limit read rate (IO per second) from a device (default []) +      --device-write-bps list          Limit write rate (bytes per second) to a device (default []) +      --device-write-iops list         Limit write rate (IO per second) to a device (default []) +      --disable-content-trust          Skip image verification (default true) +      --dns list                       Set custom DNS servers +      --dns-option list                Set DNS options +      --dns-search list                Set custom DNS search domains +      --domainname string              Container NIS domain name +      --entrypoint string              Overwrite the default ENTRYPOINT of the image +  -e, --env list                       Set environment variables +      --env-file list                  Read in a file of environment variables +      --expose list                    Expose a port or a range of ports +      --gpus gpu-request               GPU devices to add to the container ('all' to pass all GPUs) +      --group-add list                 Add additional groups to join +      --health-cmd string              Command to run to check health +      --health-interval duration       Time between running the check (ms|s|m|h) (default 0s) +      --health-retries int             Consecutive failures needed to report unhealthy +      --health-start-period duration   Start period for the container to initialize before starting health-retries countdown (ms|s|m|h) (default 0s) +      --health-timeout duration        Maximum time to allow one check to run (ms|s|m|h) (default 0s) +      --help                           Print usage +  -h, --hostname string                Container host name +      --init                           Run an init inside the container that forwards signals and reaps processes +  -i, --interactive                    Keep STDIN open even if not attached +      --ip string                      IPv4 address (e.g., 172.30.100.104) +      --ip6 string                     IPv6 address (e.g., 2001:db8::33) +      --ipc string                     IPC mode to use +      --isolation string               Container isolation technology +      --kernel-memory bytes            Kernel memory limit +  -l, --label list                     Set meta data on a container +      --label-file list                Read in a line delimited file of labels +      --link list                      Add link to another container +      --link-local-ip list             Container IPv4/IPv6 link-local addresses +      --log-driver string              Logging driver for the container +      --log-opt list                   Log driver options +      --mac-address string             Container MAC address (e.g., 92:d0:c6:0a:29:33) +  -m, --memory bytes                   Memory limit +      --memory-reservation bytes       Memory soft limit +      --memory-swap bytes              Swap limit equal to memory plus swap: '-1' to enable unlimited swap +      --memory-swappiness int          Tune container memory swappiness (0 to 100) (default -1) +      --mount mount                    Attach a filesystem mount to the container +      --name string                    Assign a name to the container +      --network network                Connect a container to a network +      --network-alias list             Add network-scoped alias for the container +      --no-healthcheck                 Disable any container-specified HEALTHCHECK +      --oom-kill-disable               Disable OOM Killer +      --oom-score-adj int              Tune host's OOM preferences (-1000 to 1000) +      --pid string                     PID namespace to use +      --pids-limit int                 Tune container pids limit (set -1 for unlimited) +      --platform string                Set platform if server is multi-platform capable +      --privileged                     Give extended privileges to this container +  -p, --publish list                   Publish a container's port(s) to the host +  -P, --publish-all                    Publish all exposed ports to random ports +      --read-only                      Mount the container's root filesystem as read only +      --restart string                 Restart policy to apply when a container exits (default "no") +      --rm                             Automatically remove the container when it exits +      --runtime string                 Runtime to use for this container +      --security-opt list              Security Options +      --shm-size bytes                 Size of /dev/shm +      --sig-proxy                      Proxy received signals to the process (default true) +      --stop-signal string             Signal to stop a container (default "SIGTERM") +      --stop-timeout int               Timeout (in seconds) to stop a container +      --storage-opt list               Storage driver options for the container +      --sysctl map                     Sysctl options (default map[]) +      --tmpfs list                     Mount a tmpfs directory +  -t, --tty                            Allocate a pseudo-TTY +      --ulimit ulimit                  Ulimit options (default []) +  -u, --user string                    Username or UID (format: [:]) +      --userns string                  User namespace to use +      --uts string                     UTS namespace to use +  -v, --volume list                    Bind mount a volume +      --volume-driver string           Optional volume driver for the container +      --volumes-from list              Mount volumes from the specified container(s) +  -w, --workdir string                 Working directory inside the container +``` + +比如我们要执行python的shell,需要添加-it参数,即:docker run -it python:3.8 + +``` +$ docker run -it python:3.8  +Python 3.8.7 (default, Dec 22 2020, 18:46:25)  +[GCC 8.3.0] on linux +Type "help", "copyright", "credits" or "license" for more information. +>>>  +``` + +#### 4.2 将宿主机的文件挂载到容器 + +docker容器与宿主机是隔离的,要想让容器内的程序能访问宿主机上的文件,需要通过-v参数将宿主机的文件挂载到容器中。 + +比如我们在宿主机上有一个hello.py,可以打印hello,想要在python容器中执行,就需要进行挂载。-v后还需要接两个参数,分别是宿主机的目录和容器内的目录,两者使用:分隔,路径必须都是绝对路径。 + +我的hello.py保存在主目录的/docker_test目录中,将这个目录挂载到容器的/docker_test目录,然后在容器内执行python /docker_test/hello.py: + +``` +$ docker run -v ~/docker_test:/docker_test python:3.8 python /docker_test/hello.py +hello +``` + +#### 4.3 容器管理 + +上面的服务运行之后,可以使用docker ps命令,查看运行中的容器: + +``` +$ docker ps +CONTAINER ID     IMAGE           COMMAND                  CREATED           STATUS         PORTS                    NAMES +ec4c86b8a163     python:3.8      "python /docker_test…"   5 seconds ago     Up 4 seconds   0.0.0.0:5000->5000/tcp   eager_wilson +``` + +显示的内容有下面几列: + +* CONTAINER ID:容器ID +* IMAGE:镜像名称和版本 +* COMMAND:执行的命令 +* CREATED:容器创建时间 +* STATUS:容器的状态 +* PORTS:端口映射 +* NAMES:容器名 + +要想结束容器,可以使用docker kill 容器ID命令。 + +### 自制Docker镜像 + +一般而言,当我们的程序开发完成后,会连同程序文件与运行环境一起制作成一个新的镜像。 + +要制作镜像,需要编写Dockerfile。DockeFile由多个命令组成,常用的命令有: + +* FROM:基于某个镜像来制作新的镜像。格式为:FROM 镜像名称:镜像版本。 +* COPY:从宿主机复制文件,支持?、*等通配符。格式为:COPY 源文件路径 目标文件路径。 +* ADD:从宿主机添加文件,格式与COPY相同,区别在于当文件为压缩文件时,会解压缩到目标路径。 +* RUN:在创建新镜像的过程中执行的shell命令。格式为:RUN shell命令行。注意,此shell命令将在容器内执行。 +* CMD:在容器实例中运行的命令,格式与RUN相同。注意,如果在docker run时指定了命令,将不会执行CMD的内容。 +* ENTRYPOINT:在容器实例中运行的命令,格式与CMD相同。注意,如果在docker run时指定了命令,该命令会以命令行参数的形式传递到ENTRYPOINT中。 +* ENV:在容器中创建环境变量,格式为:ENV 变量名值。 + +注意,Docker镜像中有一个层的概念,每执行一个RUN命令,就会创建一个层,层过多会导致镜像文件体积增大。尽量在RUN命令中使用&&连接多条shell命令,减少RUN命令的个数,可以有效减小镜像文件的体积。 + +#### 5.1 自制显示文本文件内容镜像 + +编写cat.py,接收一个文件名,由python读取文件并显示文件的内容: + +``` +import os +import sys + +input = sys.argv[1] + +with open(input, "r") as fp: +    print(fp.read()) +``` + +这个例子比较简单,缩写Dockerfile如下: + +``` +FROM python:3.8 +WORKDIR /files +COPY cat.py /cat.py +ENTRYPOINT ["python", "/cat.py"] +``` + +这个Dockerfile的含义是: + +* 以python:3.8为基础镜像 +* 容器启动命令的工作目录为/files,在运行镜像时,需要我们把宿主机的某目录挂载到容器的/files目录 +* 复制cat.py到容器的根目录下 +* 启动时运行python /cat.py命令 + +需要说明的是,ENTRYPOINT有两种写法: + +``` +ENTRYPOINT python /cat.py +ENTRYPOINT ["python", "/cat.py"] +``` + +这里采用第二种写法,是因为我们要在外部给容器传递参数。执行命令编译Docker镜像: + +``` +docker build -t cat:1.0 . +``` + +这个命令中,-t的含义是目标,即生成的镜像名为hello,版本号为1.0,别忘了最后那个.,这叫到上下文路径,是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包。 + +这样,我们的第一个镜像就制作完成了,使用下面的命令执行它: + +``` +docker run -it -v ~/docker_test/cat/files:/files cat:1.0 test.txt +``` + +即可看到~/docker_test/cat/files/test.txt的内容。 + +#### 5.2 用 Docker 部署一个 Python 应用 + +使用 Docker 部署应用的常规流程是: + +* 开发项目并本地测试通过 +* 编写 Dockerfile 放置到项目根目录 +* 打包镜像文件 +* 运行镜像容器 +* 测试 + +为了演示方便,这里以一个简单的 Flask 项目为例进行讲解 + +##### 项目开发 + +``` +from flask import Flask + +# 安装依赖 +# pip3 install -U flask + +app = Flask(__name__) + + +@app.route('/') +def index(): +    return "测试容器部署!" + + +if __name__ == '__main__': +    app.run(host='0.0.0.0', port=8888) + +# 浏览器访问测试 +# http://127.0.0.1:8888/ +``` + +项目开发完成,并在本地测试通过后就可以编写 Dockerfile 文件了 + +##### 编写 Dockerfile + +在项目根目录下,创建一个 Dockerfile 文件,使用上面的指令编写描述脚本 + +需要注意的是,这里使用「 EXPOSE 」指令暴露的端口号与入口文件定义的端口号保持一致 + +``` +# Dockerfile + +FROM centos:7.9.2009 +RUN yum makecache fast; +RUN yum install python3-devel python3-pip -y +RUN pip3 install -i https://pypi.douban.com/simple flask +COPY main.py /opt +WORKDIR /opt +EXPOSE 8888 +CMD ["python3","main.py"] +``` + +##### 构建镜像 + +``` +# 在当前文件夹下,根据Dockerfile文件构建一个镜像 +# 镜像名称:xag/my_flask_web +# --no-cache:不使用旧的缓存进行镜像构建 +docker build --no-cache -t "xag/my_flask_web" . +``` + +##### 运行镜像容器 + +使用 docker run 命令基于镜像运行一个容器 + +其中 + +* -d:代表容器在后台运行,不是基于前台运行 +* --name:用于执行容器的别名 +* -p:用于配置宿主机与容器的端口映射 + +``` +# -d:后台运行 +# 宿主机(9999)映射容器内的8888(上面Dockerfile已经暴露了8888端口) +docker run -d --name flask_web -p 9999:8888 xag/my_flask_web   +``` + +##### 测试一下 + +最后在浏览器中,通过宿主机暴露的端口号 9999 访问项目了 + +访问地址:http://127.0.0.1:9999/ + +### 导入镜像到生产环境 + +到此,我们的镜像已经制作好了,可是,镜像文件在哪,如何在生产环境下运行呢? + +刚才使用docker images命令时,已经看到了生成的镜像: + +``` +$ docker images                           +REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE +hello               1.0                 01fe19111dc7        59 minutes ago      893MB +python              3.8                 f5041c8ae6b1        13 days ago         884MB +ubuntu              20.04               f643c72bc252        5 weeks ago         72.9MB +hello-world         latest              bf756fb1ae65        12 months ago       13.3kB +``` + +我们可以使用docker save命令将镜像保存到指定的文件中,保存的文件是一个.tar格式的压缩文件: + +``` +docker save -o hello.tar hello:1.0 +``` + +将hello.tar复制到生产环境的机器上,然后执行导入命令: + +``` +docker load -i hello.tar +``` + +就可以使用了。 diff --git "a/_posts/2023-03-29-linux\345\216\213\345\212\233\346\265\213\350\257\225\347\273\274\345\220\210\346\243\200\346\265\213\346\226\271\346\241\210.md" "b/_posts/2023-03-29-linux\345\216\213\345\212\233\346\265\213\350\257\225\347\273\274\345\220\210\346\243\200\346\265\213\346\226\271\346\241\210.md" new file mode 100644 index 0000000000..577ca02376 --- /dev/null +++ "b/_posts/2023-03-29-linux\345\216\213\345\212\233\346\265\213\350\257\225\347\273\274\345\220\210\346\243\200\346\265\213\346\226\271\346\241\210.md" @@ -0,0 +1,403 @@ +--- +layout: post +title: linux压力测试综合检测方案 +date: 2023-03-29 +description: "介绍了如何使用stress对linux服务器进行压力测试" +tag: Linux +--- + + +### 对CPU进行压力测试 + +#### stress.sh + +```shell +#对CPU进行压力测试 +cpu_number=$(grep -c 'processor' /proc/cpuinfo) +#模拟CPU在内核使用率达到100%的场景。 +stress --cpu $cpu_number +``` +#### 输出结果导txt文件中(StressResult.sh) + +```shell +#!/bin/bash +# 添加当前时间信息 +echo "当前时间为$(date)" >> stress.txt +echo "模拟CPU在内核使用率达到100%的场景:" >> stress.txt + +./stress.sh & # run stress.sh in the background +pid=$! # get the PID of the background process + +SECONDS=0 # initialize SECONDS variable to 0 + +while [ $SECONDS -lt 50 ] # check if script has been running for less than 50 seconds +do + echo "Current value of SECONDS: $SECONDS" >> stress.txt + uptime >> stress.txt + #echo "\n处理器实时统计信息如下" >> stress.txt + #-P ALL参数指定该命令应报告所有处理器的统计信息 + #mpstat -P ALL 5 1 >> stress.txt + #echo "\n" >> stress.txt + SECONDS=$((SECONDS+2)) + sleep 2 +done + +kill -9 $pid # kill the background process after 50 seconds +kill $(pgrep stress) +kill $(pgrep StressResult.sh) +kill $(pgrep sleep) +``` + +#### 结果 + +```console +当前时间为2023年 03月 29日 星期三 14:34:33 CST +模拟CPU在内核使用率达到100%的场景: +Current value of SECONDS: 0 + 14:34:33 up 59 min, 1 user, load average: 2.49, 2.49, 2.14 +Current value of SECONDS: 2 + 14:34:35 up 59 min, 1 user, load average: 2.61, 2.52, 2.15 +Current value of SECONDS: 4 + 14:34:38 up 59 min, 1 user, load average: 2.61, 2.52, 2.15 +Current value of SECONDS: 6 + 14:34:40 up 59 min, 1 user, load average: 2.61, 2.52, 2.15 +Current value of SECONDS: 8 + 14:34:42 up 59 min, 1 user, load average: 2.80, 2.56, 2.16 +Current value of SECONDS: 10 + 14:34:44 up 59 min, 1 user, load average: 2.80, 2.56, 2.16 +Current value of SECONDS: 12 + 14:34:46 up 59 min, 1 user, load average: 2.98, 2.60, 2.18 +Current value of SECONDS: 14 + 14:34:48 up 59 min, 1 user, load average: 2.98, 2.60, 2.18 +Current value of SECONDS: 16 + 14:34:50 up 59 min, 1 user, load average: 2.98, 2.60, 2.18 +Current value of SECONDS: 18 + 14:34:52 up 59 min, 1 user, load average: 3.06, 2.62, 2.19 +Current value of SECONDS: 20 + 14:34:54 up 59 min, 1 user, load average: 3.06, 2.62, 2.19 +Current value of SECONDS: 22 + 14:34:56 up 59 min, 1 user, load average: 3.29, 2.68, 2.21 +Current value of SECONDS: 24 + 14:34:58 up 59 min, 1 user, load average: 3.29, 2.68, 2.21 +Current value of SECONDS: 26 + 14:35:00 up 59 min, 1 user, load average: 3.29, 2.68, 2.21 +Current value of SECONDS: 28 + 14:35:02 up 59 min, 1 user, load average: 3.43, 2.72, 2.22 +Current value of SECONDS: 30 + 14:35:04 up 59 min, 1 user, load average: 3.43, 2.72, 2.22 +Current value of SECONDS: 32 + 14:35:06 up 59 min, 1 user, load average: 3.48, 2.74, 2.23 +Current value of SECONDS: 34 + 14:35:08 up 59 min, 1 user, load average: 3.48, 2.74, 2.23 +Current value of SECONDS: 36 + 14:35:10 up 59 min, 1 user, load average: 3.52, 2.76, 2.24 +Current value of SECONDS: 38 + 14:35:12 up 1:00, 1 user, load average: 3.52, 2.76, 2.24 +Current value of SECONDS: 40 + 14:35:14 up 1:00, 1 user, load average: 3.52, 2.76, 2.24 +Current value of SECONDS: 42 + 14:35:16 up 1:00, 1 user, load average: 3.72, 2.82, 2.26 +Current value of SECONDS: 44 + 14:35:18 up 1:00, 1 user, load average: 3.72, 2.82, 2.26 +Current value of SECONDS: 46 + 14:35:20 up 1:00, 1 user, load average: 3.74, 2.84, 2.27 +Current value of SECONDS: 48 + 14:35:22 up 1:00, 1 user, load average: 3.74, 2.84, 2.27 +``` + +### 对内存进行压力测试 + +#### memory.sh + +```shell +# 新增4个内存分配进程,每次分配大小730M,分配后不释放,长期保持测试 +stress --vm 4 --vm-bytes 730M --vm-keep +``` + +#### 输出结果导txt文件中(memoryResult.sh) + +```shell +#!/bin/bash +# 添加当前时间信息 +echo "当前时间为$(date)" >> memorystress.txt +echo "# 新增4个内存分配进程,每次分配大小730M,分配后不释放,长期保持测试 " >> memorystress.txt + +./memory.sh & # run stress.sh in the background +pid=$! # get the PID of the background process + +SECONDS=0 # initialize SECONDS variable to 0 + +while [ $SECONDS -lt 50 ] # check if script has been running for less than 50 seconds +do + echo "Current value of SECONDS: $SECONDS" >> memorystress.txt + uptime >> memorystress.txt + #echo "\n处理器实时统计信息如下" >> stress.txt + #-P ALL参数指定该命令应报告所有处理器的统计信息 + #mpstat -P ALL 5 1 >> stress.txt + #echo "\n" >> stress.txt + SECONDS=$((SECONDS+2)) + sleep 2 +done + +kill -9 $pid # kill the background process after 50 seconds +kill $(pgrep stress) +kill $(pgrep memoryResult.sh) +kill $(pgrep sleep) +``` + +#### 执行memoryResult.sh + +```console +[root@localhost ~]# ./memoryResult.sh +``` + +#### 输出结果 + +```console +当前时间为2023年 03月 29日 星期三 14:32:07 CST +# 新增4个内存分配进程,每次分配大小730M,分配后不释放,长期保持测试 +Current value of SECONDS: 0 + 14:32:07 up 56 min, 1 user, load average: 2.24, 2.34, 2.02 +Current value of SECONDS: 2 + 14:32:09 up 56 min, 1 user, load average: 2.24, 2.34, 2.02 +Current value of SECONDS: 4 + 14:32:11 up 57 min, 1 user, load average: 2.46, 2.38, 2.04 +Current value of SECONDS: 6 + 14:32:13 up 57 min, 1 user, load average: 2.46, 2.38, 2.04 +Current value of SECONDS: 8 + 14:32:15 up 57 min, 1 user, load average: 2.46, 2.38, 2.04 +Current value of SECONDS: 10 + 14:32:17 up 57 min, 1 user, load average: 2.83, 2.46, 2.07 +Current value of SECONDS: 12 + 14:32:19 up 57 min, 1 user, load average: 2.83, 2.46, 2.07 +Current value of SECONDS: 14 + 14:32:21 up 57 min, 1 user, load average: 2.92, 2.48, 2.08 +Current value of SECONDS: 16 + 14:32:23 up 57 min, 1 user, load average: 2.92, 2.48, 2.08 +Current value of SECONDS: 18 + 14:32:25 up 57 min, 1 user, load average: 3.01, 2.51, 2.09 +Current value of SECONDS: 20 + 14:32:27 up 57 min, 1 user, load average: 3.01, 2.51, 2.09 +Current value of SECONDS: 22 + 14:32:29 up 57 min, 1 user, load average: 3.01, 2.51, 2.09 +Current value of SECONDS: 24 + 14:32:31 up 57 min, 1 user, load average: 3.09, 2.53, 2.10 +Current value of SECONDS: 26 + 14:32:33 up 57 min, 1 user, load average: 3.09, 2.53, 2.10 +Current value of SECONDS: 28 + 14:32:35 up 57 min, 1 user, load average: 3.16, 2.56, 2.11 +Current value of SECONDS: 30 + 14:32:37 up 57 min, 1 user, load average: 3.16, 2.56, 2.11 +Current value of SECONDS: 32 + 14:32:39 up 57 min, 1 user, load average: 3.16, 2.56, 2.11 +Current value of SECONDS: 34 + 14:32:41 up 57 min, 1 user, load average: 3.23, 2.58, 2.12 +Current value of SECONDS: 36 + 14:32:43 up 57 min, 1 user, load average: 3.23, 2.58, 2.12 +Current value of SECONDS: 38 + 14:32:45 up 57 min, 1 user, load average: 3.29, 2.61, 2.13 +Current value of SECONDS: 40 + 14:32:47 up 57 min, 1 user, load average: 3.29, 2.61, 2.13 +Current value of SECONDS: 42 + 14:32:49 up 57 min, 1 user, load average: 3.29, 2.61, 2.13 +Current value of SECONDS: 44 + 14:32:51 up 57 min, 1 user, load average: 3.35, 2.63, 2.14 +Current value of SECONDS: 46 + 14:32:53 up 57 min, 1 user, load average: 3.35, 2.63, 2.14 +Current value of SECONDS: 48 + 14:32:55 up 57 min, 1 user, load average: 3.40, 2.65, 2.15 +``` + +### 对IO进行压力测试 + +#### io.sh + +```shell +# 新增6个I/O进程,测试100秒 +stress -i 6 +``` + +#### ioResult.sh(输出结果到iostress.txt) + +```shell +#!/bin/bash +# 添加当前时间信息 +echo "当前时间为$(date)" >> iostress.txt +echo "模拟新增6个I/O进程的场景:" >> iostress.txt + +./io.sh & # run stress.sh in the background +pid=$! # get the PID of the background process + +SECONDS=0 # initialize SECONDS variable to 0 + +while [ $SECONDS -lt 50 ] # check if script has been running for less than 50 seconds +do + echo "Current value of SECONDS: $SECONDS" >> iostress.txt + uptime >> iostress.txt + #echo "\n处理器实时统计信息如下" >> stress.txt + #-P ALL参数指定该命令应报告所有处理器的统计信息 + #mpstat -P ALL 5 1 >> stress.txt + #echo "\n" >> stress.txt + SECONDS=$((SECONDS+2)) + sleep 2 +done + +kill -9 $pid # kill the background process after 50 seconds +kill $(pgrep stress) +kill $(pgrep ioResult.sh) +kill $(pgrep sleep) +``` + +#### 查看结果 + +```console +当前时间为2023年 03月 29日 星期三 14:30:31 CST +模拟新增6个I/O进程的场景: +Current value of SECONDS: 0 + 14:30:31 up 55 min, 1 user, load average: 1.69, 1.90, 1.84 +Current value of SECONDS: 2 + 14:30:33 up 55 min, 1 user, load average: 1.69, 1.90, 1.84 +Current value of SECONDS: 4 + 14:30:35 up 55 min, 1 user, load average: 1.69, 1.90, 1.84 +Current value of SECONDS: 6 + 14:30:37 up 55 min, 1 user, load average: 2.04, 1.97, 1.87 +Current value of SECONDS: 8 + 14:30:39 up 55 min, 1 user, load average: 2.04, 1.97, 1.87 +Current value of SECONDS: 10 + 14:30:41 up 55 min, 1 user, load average: 2.44, 2.05, 1.89 +Current value of SECONDS: 12 + 14:30:43 up 55 min, 1 user, load average: 2.44, 2.05, 1.89 +Current value of SECONDS: 14 + 14:30:45 up 55 min, 1 user, load average: 2.44, 2.05, 1.89 +Current value of SECONDS: 16 + 14:30:47 up 55 min, 1 user, load average: 3.04, 2.19, 1.94 +Current value of SECONDS: 18 + 14:30:49 up 55 min, 1 user, load average: 3.04, 2.19, 1.94 +Current value of SECONDS: 20 + 14:30:51 up 55 min, 1 user, load average: 3.60, 2.32, 1.98 +Current value of SECONDS: 22 + 14:30:53 up 55 min, 1 user, load average: 3.60, 2.32, 1.98 +Current value of SECONDS: 24 + 14:30:55 up 55 min, 1 user, load average: 3.60, 2.32, 1.98 +Current value of SECONDS: 26 + 14:30:57 up 55 min, 1 user, load average: 3.95, 2.41, 2.01 +Current value of SECONDS: 28 + 14:30:59 up 55 min, 1 user, load average: 3.95, 2.41, 2.01 +Current value of SECONDS: 30 + 14:31:01 up 55 min, 1 user, load average: 4.20, 2.49, 2.04 +Current value of SECONDS: 32 + 14:31:03 up 55 min, 1 user, load average: 4.20, 2.49, 2.04 +Current value of SECONDS: 34 + 14:31:05 up 55 min, 1 user, load average: 4.26, 2.53, 2.06 +Current value of SECONDS: 36 + 14:31:07 up 55 min, 1 user, load average: 4.26, 2.53, 2.06 +Current value of SECONDS: 38 + 14:31:09 up 55 min, 1 user, load average: 4.26, 2.53, 2.06 +Current value of SECONDS: 40 + 14:31:11 up 56 min, 1 user, load average: 4.40, 2.59, 2.08 +Current value of SECONDS: 42 + 14:31:13 up 56 min, 1 user, load average: 4.40, 2.59, 2.08 +Current value of SECONDS: 44 + 14:31:15 up 56 min, 1 user, load average: 4.53, 2.64, 2.10 +Current value of SECONDS: 46 + 14:31:17 up 56 min, 1 user, load average: 4.53, 2.64, 2.10 +Current value of SECONDS: 48 + 14:31:19 up 56 min, 1 user, load average: 4.53, 2.64, 2.10 +``` + +### 对磁盘IO进行压力测试 + +#### hdd.sh + +```shell +# 开启2个磁盘IO进程,每次写10GB数据到磁盘 +stress --hdd 2 --hdd-bytes 10G --backoff 2000000 +``` + +#### hddResult.sh(输出结果到hddstress.txt) + +```shell +#!/bin/bash +# 添加当前时间信息 +echo "当前时间为$(date)" >> hddstress.txt +echo "模拟开启2个磁盘IO进程,每次写10GB数据到磁盘的场景:" >> hddstress.txt + +./hdd.sh & # run stress.sh in the background +pid=$! # get the PID of the background process + +SECONDS=0 # initialize SECONDS variable to 0 + +while [ $SECONDS -lt 50 ] # check if script has been running for less than 50 seconds +do + echo "Current value of SECONDS: $SECONDS" >> hddstress.txt + uptime >> hddstress.txt + #echo "\n处理器实时统计信息如下" >> stress.txt + #-P ALL参数指定该命令应报告所有处理器的统计信息 + #mpstat -P ALL 5 1 >> stress.txt + #echo "\n" >> stress.txt + SECONDS=$((SECONDS+2)) + sleep 2 +done + +kill -9 $pid # kill the background process after 50 seconds +kill $(pgrep stress) +kill $(pgrep hddResult.sh) +kill $(pgrep sleep) +``` + +#### 查看结果 + +```console +当前时间为2023年 03月 29日 星期三 14:33:22 CST +模拟开启2个磁盘IO进程,每次写10GB数据到磁盘的场景: +Current value of SECONDS: 0 + 14:33:22 up 58 min, 1 user, load average: 2.24, 2.44, 2.09 +Current value of SECONDS: 2 + 14:33:24 up 58 min, 1 user, load average: 2.24, 2.44, 2.09 +Current value of SECONDS: 4 + 14:33:26 up 58 min, 1 user, load average: 2.22, 2.43, 2.09 +Current value of SECONDS: 6 + 14:33:28 up 58 min, 1 user, load average: 2.22, 2.43, 2.09 +Current value of SECONDS: 8 + 14:33:30 up 58 min, 1 user, load average: 2.22, 2.43, 2.09 +Current value of SECONDS: 10 + 14:33:32 up 58 min, 1 user, load average: 2.28, 2.44, 2.10 +Current value of SECONDS: 12 + 14:33:34 up 58 min, 1 user, load average: 2.28, 2.44, 2.10 +Current value of SECONDS: 14 + 14:33:36 up 58 min, 1 user, load average: 2.34, 2.45, 2.10 +Current value of SECONDS: 16 + 14:33:38 up 58 min, 1 user, load average: 2.34, 2.45, 2.10 +Current value of SECONDS: 18 + 14:33:40 up 58 min, 1 user, load average: 2.39, 2.46, 2.11 +Current value of SECONDS: 20 + 14:33:42 up 58 min, 1 user, load average: 2.39, 2.46, 2.11 +Current value of SECONDS: 22 + 14:33:44 up 58 min, 1 user, load average: 2.39, 2.46, 2.11 +Current value of SECONDS: 24 + 14:33:46 up 58 min, 1 user, load average: 2.44, 2.47, 2.11 +Current value of SECONDS: 26 + 14:33:48 up 58 min, 1 user, load average: 2.44, 2.47, 2.11 +Current value of SECONDS: 28 + 14:33:51 up 58 min, 1 user, load average: 2.49, 2.48, 2.12 +Current value of SECONDS: 30 + 14:33:53 up 58 min, 1 user, load average: 2.49, 2.48, 2.12 +Current value of SECONDS: 32 + 14:33:55 up 58 min, 1 user, load average: 2.53, 2.49, 2.12 +Current value of SECONDS: 34 + 14:33:58 up 58 min, 1 user, load average: 2.53, 2.49, 2.12 +Current value of SECONDS: 36 + 14:34:01 up 58 min, 1 user, load average: 2.49, 2.48, 2.12 +Current value of SECONDS: 38 + 14:34:03 up 58 min, 1 user, load average: 2.49, 2.48, 2.12 +Current value of SECONDS: 40 + 14:34:04 up 58 min, 1 user, load average: 2.49, 2.48, 2.12 +Current value of SECONDS: 42 + 14:34:09 up 58 min, 1 user, load average: 2.45, 2.47, 2.12 +Current value of SECONDS: 44 + 14:34:12 up 59 min, 1 user, load average: 2.49, 2.48, 2.12 +Current value of SECONDS: 46 + 14:34:17 up 59 min, 1 user, load average: 2.93, 2.57, 2.15 +Current value of SECONDS: 48 + 14:34:19 up 59 min, 1 user, load average: 2.93, 2.57, 2.15 +``` diff --git "a/_posts/2023-03-29-\344\275\216\345\216\213\347\224\265\345\267\245.md" "b/_posts/2023-03-29-\344\275\216\345\216\213\347\224\265\345\267\245.md" new file mode 100644 index 0000000000..e5e407418b --- /dev/null +++ "b/_posts/2023-03-29-\344\275\216\345\216\213\347\224\265\345\267\245.md" @@ -0,0 +1,12 @@ +--- +layout: post + +title: 低压电工知识点汇总 + +date: 2023-03-29 + +description: "介绍了低压电工理论考试知识点" + +tag: 工具箱 +--- +# 控制电路 diff --git "a/_posts/2023-04-03-\344\275\277\347\224\250cotpilot\345\222\214codecursor\346\227\240\344\273\243\347\240\201\345\237\272\347\241\200\344\271\237\350\203\275\350\275\273\346\235\276\347\274\226\347\250\213\357\274\210\345\244\204\347\220\206\345\255\227\347\254\246\344\270\262\357\274\211.md" "b/_posts/2023-04-03-\344\275\277\347\224\250cotpilot\345\222\214codecursor\346\227\240\344\273\243\347\240\201\345\237\272\347\241\200\344\271\237\350\203\275\350\275\273\346\235\276\347\274\226\347\250\213\357\274\210\345\244\204\347\220\206\345\255\227\347\254\246\344\270\262\357\274\211.md" new file mode 100644 index 0000000000..d5c844f7c4 --- /dev/null +++ "b/_posts/2023-04-03-\344\275\277\347\224\250cotpilot\345\222\214codecursor\346\227\240\344\273\243\347\240\201\345\237\272\347\241\200\344\271\237\350\203\275\350\275\273\346\235\276\347\274\226\347\250\213\357\274\210\345\244\204\347\220\206\345\255\227\347\254\246\344\270\262\357\274\211.md" @@ -0,0 +1,168 @@ +--- +layout: post +title: 使用cotpilot和codecursor无代码基础也能轻松编程(处理字符串) +date: 2023-04-03 +description: "介绍了如何使用使用cotpilot和codecursor对字符串进行操作" +tag: 工具箱 +--- +## 1. 随机生成一个随机的string + +![20230403105015](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230403105015.png) + +![20230403105045](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230403105045.png) + +结果如下:(根据和github里面搜出来的代码块基本上是一样的,挺简洁的) + +![20230403105129](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230403105129.png) + +调整一下就可以直接使用 + +```python +import random +import string + +# generate a string of random characters +def generate_random_string(length): + letters = string.ascii_lowercase + result_str = ''.join(random.choice(letters) for i in range(length)) + return result_str + +print(generate_random_string(10)) +``` + +运行代码,结果如下: + +![20230403105320](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230403105320.png) + +## 2. 对字符串进行切片 + +首先,我大概想了下以后可能要处理的文件的文件名,可能是这个格式,最开始是设备的安装日期,然后是地点,机组号码,传感器编号,最后是这个装置的布防期间,大概是这样滴。 + +``string1= '2015-01-01-内蒙-机组304-308传感器-测试期间-from-2016-03-04-to-2018-05-07'`` + +接着,我希望codecursor帮我写代码切割字符串: + +![20230403105632](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230403105632.png) + +得到的建议代码如下: + +![20230403105727](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230403105727.png) + +print一下结果发现是可以直接用的,已经切割出了符合我们预期的substring: + +![20230403105839](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230403105839.png) + +```python +string1= '2015-01-01-内蒙-机组304-308传感器-测试期间-from-2016-03-04-to-2018-05-07' + + +year = string1[0:4] +month = string1[5:7] +day = string1[8:10] +region = string1[11:13] +unit = string1[14:19] +sensor = string1[20:26] +start_date = string1[37:47] +end_date = string1[51:61] +print(year, month, day, region, unit, sensor,start_date, end_date) +``` + +但是我试了下,在大部分情况下,他提取的substring都有问题,最好是print出来看看,然后再调整一下。 + +## 3. 批量提取字符串中的信息 + +我们距离目标很近了,有时候我们期待我们可以对多个文件进行操作,那么这时候我们可能需要构造一个提取信息的函数帮助我们完成这个任务。 + +首先,我们生成一系列和string1格式相同的字符串,看看codecursor能不能帮我们做到这一点: + +![20230403110300](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230403110300.png) + +![20230403110418](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230403110418.png) + +神了,它居然这都可以做到。 + +然后,我们构造提取信息的函数,在这里就不演示codecursor的操作了,直接上它帮助我们写好的代码: + +```python +# 生成新的字符串 + +string3 = '2022-01-01-北京-机组101-305传感器-测试期间-from-2023-03-04-to-2025-05-07' +string4 = '2023-02-01-上海-机组102-306传感器-测试期间-from-2024-03-04-to-2026-05-07' +string5 = '2024-03-01-广州-机组103-307传感器-测试期间-from-2025-03-04-to-2027-05-07' +string6 = '2025-04-01-深圳-机组104-308传感器-测试期间-from-2026-03-04-to-2028-05-07' +string7 = '2026-05-01-成都-机组105-309传感器-测试期间-from-2027-03-04-to-2029-05-07' +string8 = '2027-06-01-重庆-机组106-310传感器-测试期间-from-2028-03-04-to-2030-05-07' + +# 构造提取信息的函数 +def extract_info(string): + year = string[0:4] + month = string[5:7] + day = string[8:10] + region = string[11:13] + unit = string[14:19] + sensor = string[20:26] + start_date = string[37:47] + end_date = string[51:61] + return year, month, day, region, unit, sensor, start_date, end_date +year, month, day, region, unit, sensor, start_date, end_date = extract_info(string3) +print(year, month, day, region, unit, sensor, start_date, end_date) +``` + +运行结果如下: + +![20230403110647](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230403110647.png) + +## 4.批量保存提取的信息 + +还是跳过操作步骤,给出codecursor给的代码: + +```python +# 将列表转换为表格 +string_list = [string1,string3, string4, string5, string6, string7, string8] +table = [] +for string in string_list: + info = extract_info(string) + table.append(info) +print(table) + +# 将表格转换为DataFrame,并保存为csv文件 +import pandas as pd +df = pd.DataFrame(table, columns=['year', 'month', 'day', 'region', 'unit', 'sensor', 'start_date', 'end_date']) +df.to_csv('string.csv', index=False) +``` + +运行代码,结果如下: + +![20230403110911](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230403110911.png) + +## 5.其他探索 + +生成新的字符串那一步我不太满意,这次我希望他可以帮助我自动生成和string1一样格式的字符串,它给出的代码是这样的: + +```python +import random +def generate_random_string(): + year = str(random.randint(2022, 2027)) + month = str(random.randint(1, 12)).zfill(2) + day = str(random.randint(1, 28)).zfill(2) + region = random.choice(['北京', '上海', '广州', '深圳', '成都', '重庆']) + unit = '机组' + str(random.randint(101, 106)) + sensor = str(random.randint(305, 310)) + '传感器' + start_year = str(random.randint(int(year)+1, 2029)) # int()方法用于将字符串转换为整数, int(year)+1表示将字符串转换为整数后加1 + start_month = str(random.randint(1, 12)).zfill(2) # zfill()方法用于将字符串的长度补全为指定的长度,不足的部分用0填充 + start_day = str(random.randint(1, 28)).zfill(2) + end_year = str(random.randint(int(start_year)+1, 2030)) + end_month = str(random.randint(1, 12)).zfill(2) + end_day = str(random.randint(1, 28)).zfill(2) + string = year + '-' + month + '-' + day + '-' + region + '-' + unit + '-' + sensor + '-测试期间-from-' + start_year + '-' + start_month + '-' + start_day + '-to-' + end_year + '-' + end_month + '-' + end_day + return string + +string_list = [] +for i in range(20): + string_list.append(generate_random_string()) +print(string_list) +``` + +运行结果如下: + +![20230403111413](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230403111413.png) diff --git "a/_posts/2023-04-04-Linux\346\234\215\345\212\241\345\231\250\346\243\200\346\265\213\345\260\217\345\267\245\345\205\267.md" "b/_posts/2023-04-04-Linux\346\234\215\345\212\241\345\231\250\346\243\200\346\265\213\345\260\217\345\267\245\345\205\267.md" new file mode 100644 index 0000000000..411bbbcb13 --- /dev/null +++ "b/_posts/2023-04-04-Linux\346\234\215\345\212\241\345\231\250\346\243\200\346\265\213\345\260\217\345\267\245\345\205\267.md" @@ -0,0 +1,62 @@ +--- +layout: post +title: Linux服务器检测小工具 +date: 2023-04-04 +description: "介绍在Ubuntu上安装Cockpit对服务器进行监控" +tag: Linux +--- +## 安装Cockpit + +```bash +sudo apt install cockpit -y +``` + +-y参数表示自动安装,不需要手动输入y。 + +或者使用如下命令进行安装,/etc/os-release表示系统版本信息,${VERSION_CODENAME}表示系统版本代号,如Ubuntu 18.04的版本代号为bionic。 + +```bash +. /etc/os-release +sudo apt install -t ${VERSION_CODENAME}-backports cockpit +``` + +## 启动Cockpit + +```bash +sudo systemctl start cockpit +#sudo systemctl enable cockpit + +``` + +## 查看状态 + +```bash +apt -qq list cockpit # 查看版本 +sudo systemctl status cockpit +``` + +## 设置防火墙 + +```bash +ss -tunlp | grep 9090 +``` + +ss 表示查看socket状态,-t表示TCP协议,-u表示UDP协议,-n表示不解析IP地址,-l表示监听状态,-p表示显示进程信息。grep 9090表示过滤出端口为9090的进程。如果没有显示9090端口,则表示9090端口没有被占用,可以直接使用。如果显示了9090端口,则需要修改端口号。 + +```bash +sudo ufw allow 9090/tcp +``` + +/tcp表示TCP协议,/udp表示UDP协议。 + +## 访问Cockpit + +使用ifconfig命令查看服务器IP地址,然后在浏览器中输入服务器IP地址加端口9090,如http://IP_OF_SERVER:9090。 + +一旦你登录后,你可以看到 Cockpit 的主界面,在这里可以直观的看到服务器的运行状态,包括CPU使用率、内存使用率、硬盘使用率等。 + +在cockpit中我们可以很方便的管理各系统服务,包括磁盘管理,网络管理,系统服务(包括定时任务管理),浏览和搜索系统日志,升级软件包到最新版本等等。 + + + + diff --git "a/_posts/2023-04-04-\344\271\260\350\275\246\345\220\227\357\274\2102022\345\271\264\344\271\230\347\224\250\350\275\246\351\224\200\351\207\217\345\222\214\345\223\201\347\211\214\345\210\206\346\236\220).md" "b/_posts/2023-04-04-\344\271\260\350\275\246\345\220\227\357\274\2102022\345\271\264\344\271\230\347\224\250\350\275\246\351\224\200\351\207\217\345\222\214\345\223\201\347\211\214\345\210\206\346\236\220).md" new file mode 100644 index 0000000000..aff542f2d8 --- /dev/null +++ "b/_posts/2023-04-04-\344\271\260\350\275\246\345\220\227\357\274\2102022\345\271\264\344\271\230\347\224\250\350\275\246\351\224\200\351\207\217\345\222\214\345\223\201\347\211\214\345\210\206\346\236\220).md" @@ -0,0 +1,227 @@ +--- +layout: post +title: 买车吗(2022年乘用车销量和品牌分析) +date: 2023-04-04 +description: "对中国2022年乘用车销售情况进行分析,提供买车时候的决策" +tag: Python +--- +## 数据来源 + +数据来源于[和鲸社区-2022年中国狭义乘用车分车型销量数据](https://www.heywhale.com/mw/dataset/63c52c6d56eb6dde2583da2e/file)。 + +## 查看数据 + +```python +import pandas as pd +df = pd.read_csv('car_sales_2022.csv', names=['rank', 'model', 'manufacturer', 'category', 'segment', 'is_new_energy', 'is_pure_electric', 'price', 'price_min', 'price_max', 'price_mean', 'sales'], header=0) +print(df.head(10)) +import matplotlib.pyplot as plt +plt.rcParams['font.sans-serif'] = ['SimHei'] +plt.rcParams['axes.unicode_minus'] = False +``` + +结果如下: + +![20230404085334](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230404085334.png) + +## 数据清理 + +```python +print("数据清理前行数:", len(df)) +df.drop(df[(df['price_mean'] ==0) | (df['price_mean'].isna())].index, inplace=True) +print("数据清理后行数:", len(df)) +``` + +结果如下: + +```console +数据清理前行数: 612 +数据清理后行数: 607 +``` + +## 数据查看 + +### 按品牌统计车的数量 + +```python +print(df.groupby('manufacturer')['model'].count().sort_values(ascending=False)) +``` + +结果如下: + +```console +manufacturer +长安汽车 24 +长城汽车 23 +比亚迪 20 +吉利汽车 20 +上汽集团 17 + .. +云度新能源 1 +云雀汽车 1 +华晨鑫源 1 +创维汽车 1 +智己汽车 1 +Name: model, Length: 106, dtype: int64 +``` + +### 查看汽车类型 + +```python +# 查看category的取值 +print(df['category'].unique()) +# 画柱状图,显示category在不同价格区间的车的数量 +plt.figure(figsize=(10, 6)) +plt.bar(df['category'].unique(), df.groupby('category')['model'].count()) +# ['model'] 为列名,['category'] 为行名 +plt.title('不同车型的数量') +``` + +结果如下: + +![20230404085930](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230404085930.png) + +```python +#以category为行名,model为列名,显示销量 +pd.pivot_table(df, values='sales', index='category', columns='model') +``` + +结果如下: + +![20230404090144](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230404090144.png) + +```python +# 查看segment的取值 +print(df['segment'].unique()) +# 画饼状图,显示segment的占比 +plt.figure(figsize=(6, 6)) +plt.pie(df.groupby('segment')['model'].count(), labels=df['segment'].unique(), autopct='%1.1f%%') +plt.title('车型的车身类型占比') +``` + +结果如下: + +![20230404090252](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230404090252.png) + +### 查看新能源车的数量 + +```python +# 查看新能源车和非新能源车的销售量 +print(df.groupby('is_new_energy')['sales'].sum()) + +# 查看新能源车和非新能源车的销售量占比 +print(df.groupby('is_new_energy')['sales'].sum() / df['sales'].sum()) + +# 画图 + +plt.figure(figsize=(6, 6)) +plt.pie(df.groupby('is_new_energy')['sales'].sum(), labels=['非新能源车', '新能源车'], autopct='%1.1f%%') +plt.title('新能源车和非新能源车的销售量占比') +plt.show() +``` + +结果如下: + +```console +is_new_energy +False 14619974 +True 5512203 +Name: sales, dtype: int64 +is_new_energy +False 0.726199 +True 0.273801 +Name: sales, dtype: float64 +``` + +![20230404090401](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230404090401.png) + +## 按价格区间统计车的品牌和数量 + +```python +# 按价格区间统计车的品牌和数量 +bins = [5, 10, 15, 20, 30, 50, 100] +labels = ['5-10', '10-15', '15-20', '20-30', '30-50', '50-100'] +df['price_range'] = pd.cut(df['price_mean'], bins=bins, labels=labels) +print(df.groupby('price_range')['model'].value_counts()) + +plt.figure(figsize=(10,6)) +top_30 = df.nlargest(30, 'sales') # 取销量前30的车型 +plt.scatter(top_30['price_mean'], top_30['sales'], s=100, alpha=0.5, c=top_30['sales'], cmap='Reds') +plt.colorbar(label='销量(台)') + +for i, txt in enumerate(top_30['model']): + #plt.annotate(txt, (top_30['price_mean'].iloc[i], top_30['sales'].iloc[i])) + plt.annotate(txt, (top_30['price_mean'].iloc[i], top_30['sales'].iloc[i]), xytext=(5, 5), textcoords='offset points', fontsize=8, ha='center') + # 标注车型 enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列, + # 同时列出数据和数据下标,一般用在 for 循环当中。 + # iloc[] 通过行号来获取行数据 + +plt.xlabel('平均价格') +plt.ylabel('销量') +plt.title('销量前30的车型价格与销量关系散点图') +plt.show() +``` + +结果如下: + +```console +price_range model +5-10 VGV U70 1 + VGV U70Pro 1 + YARiS L 致享 1 + YARiS L 致炫 1 + YOUNG光小新 1 + .. +50-100 红旗H9 1 + 英菲尼迪QX60 1 + 蔚来ES7 1 + 蔚来ES8 1 + 飞行家 1 +Name: model, Length: 597, dtype: int64 +``` + +![20230404090646](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230404090646.png) + +## 查看比亚迪销售情况 + +```python +print(df[df['manufacturer']=='比亚迪'][['model', 'price_mean', 'sales']]) +plt.figure(figsize=(10,6)) +df_byd = df[df['manufacturer']=='比亚迪'] +plt.scatter(df_byd['price_mean'], df_byd['sales'], s=100, alpha=0.5, c='blue') +for i, txt in enumerate(df_byd['model']): + plt.annotate(txt, (df_byd['price_mean'].iloc[i], df_byd['sales'].iloc[i])) + +plt.xlabel('价格') +plt.ylabel('销量') +plt.title('比亚迪车型价格与销量关系散点图') +plt.show() +``` + +结果如下: + +```console + model price_mean sales +2 宋PLUS DM-i 18.68 388048 +13 海豚 12.68 204226 +15 秦PLUS DM-i 13.98 188522 +16 元PLUS 15.38 167220 +28 汉EV 27.58 143895 +35 汉DM 26.98 128524 +39 唐DM 27.08 125368 +47 秦PLUS EV 16.13 119755 +96 宋PLUS EV 19.53 66935 +102 驱逐舰 05 13.98 61949 +121 海豹 25.13 51200 +160 秦EV 14.98 33665 +181 元Pro 10.88 26313 +199 唐EV 31.28 23217 +226 宋Pro DM-i 15.33 20323 +242 比亚迪e2 12.33 17790 +308 宋MAX DM-i 16.13 11037 +385 比亚迪e3 15.48 5726 +488 护卫舰 07 24.63 1805 +529 比亚迪D1 16.28 994 +``` + +![20230404090910](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230404090910.png) diff --git "a/_posts/2023-04-04-\345\244\252\346\271\226\347\253\2312001-2006\345\271\264\346\260\264\350\264\250\347\233\221\346\265\213\346\225\260\346\215\256\345\210\206\346\236\220.md" "b/_posts/2023-04-04-\345\244\252\346\271\226\347\253\2312001-2006\345\271\264\346\260\264\350\264\250\347\233\221\346\265\213\346\225\260\346\215\256\345\210\206\346\236\220.md" new file mode 100644 index 0000000000..ff3cf65152 --- /dev/null +++ "b/_posts/2023-04-04-\345\244\252\346\271\226\347\253\2312001-2006\345\271\264\346\260\264\350\264\250\347\233\221\346\265\213\346\225\260\346\215\256\345\210\206\346\236\220.md" @@ -0,0 +1,218 @@ +--- +layout: post +title: 太湖站2001-2006年水质监测数据分析 +date: 2023-04-04 +description: "太湖站2001-2006年水质监测数据分析" +tag: Python +--- +## 1. 数据来源 + +[和鲸社区-太湖站2001-2006年水质监测数据集](https://www.heywhale.com/mw/dataset/626155316731be0017382112) + +包括两个文件: + +1. 太湖站水质观测站点数据.xlsx +2. 2001-2006年太湖水体物理及水体化学监测数据.xlsx + + +## 2. 站点分布示意图 + +```python +import pandas as pd +import folium +import matplotlib.pyplot as plt +# 1. 读取数据 +df = pd.read_excel(r'.\太湖站水质观测站点数据.xlsx') +print(df.head()) + +# Drop the second row of the dataframe to remove the header +df = df.drop([0]) +# Reset the index of the dataframe +df = df.reset_index(drop=True) + + +# 2. 画出站点分布 + +# Create a map object centered at (31.3, 120.6) +# Set the range of longitude and latitude for the map +east_min = df['东经'].min() - 5 +east_max = df['东经'].max() + 5 +north_min = df['北纬'].min() - 5 +north_max = df['北纬'].max() + 5 + +# Create a map object centered at with the new range of longitude and latitude +map = folium.Map(location=[(north_max+north_min)/2, (east_max+east_min)/2], zoom_start=10, control_scale=True, tiles='Stamen Terrain') +# tiles表示地图的样式;max_bounds表示地图的最大范围;control_scale表示是否显示比例尺 +# Add markers to the map for each observation station +for index, row in df.iterrows(): + folium.Marker(location=[row['北纬'], row['东经']], tooltip=row['观测站代码']).add_to(map) + +# Display the map +map + +# Save the map as an HTML file +map.save('map.html') +``` + +![20230404192804](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230404192804.png) + +## 3. 物理性质数据分析 + +对水深随着时间变化进行探究。 + +```python +import pandas as pd +import matplotlib.pyplot as plt +# 读入2001-2006年太湖水体物理及水体化学监测数据.xlsx中的表格2001-2006年太湖水体物理监测数据 +df2 = pd.read_excel(r'.\2001-2006年太湖水体物理及水体化学监测数据.xlsx', sheet_name='2001-2006年太湖水体物理监测数据') +df2 = df2.drop([0]) +# Reset the index of the dataframe +df2 = df2.reset_index(drop=True) +# Round the values in the '年' column to the nearest integer +df2['年'] = df2['年'].astype(int) +df2['月'] = df2['月'].astype(int) +# Combine the '年' and '月' columns into a single 'date' column in df2 +df2['date'] = pd.to_datetime({'year': df2['年'], 'month': df2['月'], 'day': 1}) +print(df2.head()) + +# 画出水深随时间变化的折线图 +df2_THL00 = df2[df2['站点'] == 'THL00'] +df2_THL00 = df2_THL00.set_index('date') +df2_THL00['水深'].plot() +# title表示图的标题;xlabel表示x轴的标题;ylabel表示y轴的标题 +plt.title('THL00 Water Depth') +plt.xlabel('Date') +plt.ylabel('Water Depth (m)') + +# 画出所有的站点水深随时间变化的折线图 +for station in df2['站点'].unique(): + df2_station = df2[df2['站点'] == station] + df2_station = df2_station.set_index('date') + df2_station['水深'].plot() + plt.title(station + ' Water Depth') + plt.xlabel('Date') + plt.ylabel('Water Depth (m)') + plt.show() + +# 画在一张图上 +# 设置图的大小 +plt.figure(figsize=(10, 10)) +for station in df2['站点'].unique(): + df2_station = df2[df2['站点'] == station] + df2_station = df2_station.set_index('date') + df2_station['水深'].plot() +plt.title('Water Depth') +plt.xlabel('Date') +plt.ylabel('Water Depth (m)') +# 设置legend的位置在图的外面 +plt.legend(df2['站点'].unique(), loc='center left', bbox_to_anchor=(1, 0.5)) +plt.show() +``` + +输出结果部分: + + +![20230404193002](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230404193002.png) + +![20230404193024](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230404193024.png) + +![20230404193054](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230404193054.png) + +## 4. 化学性质数据分析 + +对PH值进行探究,使用克里金插值的方法。 + +```python +import pandas as pd +# 读入2001-2006年太湖水体物理及水体化学监测数据.xlsx中的表格2001-2006年太湖水体物理监测数据 +df3 = pd.read_excel(r'.\2001-2006年太湖水体物理及水体化学监测数据.xlsx', sheet_name='2001-2006年太湖水体化学监测数据') +df3 = df3.drop([0]) +# Reset the index of the dataframe +df3 = df3.reset_index(drop=True) +# Round the values in the '年' column to the nearest integer +df3['年'] = df3['年'].astype(int) +df3['月'] = df3['月'].astype(int) +# Combine the '年' and '月' columns into a single 'date' column in df3 +df3['date'] = pd.to_datetime({'year': df3['年'], 'month': df3['月'], 'day': 1}) +df3=df3.set_index('date') +print(df3.head()) + + +# 读取站点位置 +df = pd.read_excel(r'.\太湖站水质观测站点数据.xlsx') +# Drop the second row of the dataframe to remove the header +df = df.drop([0]) +# Reset the index of the dataframe +df = df.reset_index(drop=True) + +# Rename the columns +df['站点'] = df['观测站代码'] +# drop columns +df = df.drop(['观测站序号','观测站代码'], axis=1) # axis=1表示列 +print(df.head()) + +# 提取df3中的两列,分别是站点和PH值 +df3_ph = df3[['站点', 'pH']] + +df3_ph_20010101 = df3_ph[df3_ph.index == '2001-01-01'] + +# 根据站点拼接df3_ph_20010101和df +df_merged = pd.merge(df, df3_ph_20010101, on='站点', how='left') + +# 克里金插值,df_merged中的ph +# 克里金插值需要使用外部库pykrige,需要先安装 +# pip install pykrige + +# 导入外部库 +import numpy as np +from pykrige.ok import OrdinaryKriging + + +# 将df_merged中的经纬度和pH值提取出来 +X = df_merged[['东经', '北纬']].values +y = df_merged['pH'].values + +# 设置克里金插值的网格范围 +grid_lon = np.arange(df_merged['东经'].min()-0.1, df_merged['东经'].max()+0.1, 0.01) +grid_lat = np.arange(df_merged['北纬'].min()-0.1, df_merged['北纬'].max()+0.1, 0.01) + + +# Generate ordinary kriging object +OK = OrdinaryKriging( + np.array(X[:, 0]), + np.array(X[:, 1]), + y, + variogram_model = "linear", + verbose = False, + enable_plotting = False, + coordinates_type = "euclidean", +) + +# 进行克里金插值 +z, ss = OK.execute('grid', grid_lon, grid_lat) + +# 将插值结果可视化 +plt.figure(figsize=(10, 10)) +# pcolor of z +plt.pcolor(grid_lon, grid_lat, z, cmap='jet') +plt.colorbar() +#plt.scatter(X[:, 0], X[:, 1], c=y, cmap='jet', edgecolor='k') +plt.title('pH Interpolation') +plt.xlabel('Longitude') +plt.ylabel('Latitude') +plt.show() +``` + +部分结果输出如下: + +![1680608067436](/_posts/image/2023-04-04-太湖站2001-2006年水质监测数据分析/1680608067436.png) + +![1680608067436](image/2023-04-04-太湖站2001-2006年水质监测数据分析/1680608067436.png) + + +![20230404193505](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230404193505.png) + + +克里金插值结果如下:(感觉是错的,不改了) + +![20230404193731](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230404193731.png) \ No newline at end of file diff --git "a/_posts/2023-04-05-stable diffusion\345\205\245\351\227\250\346\225\231\347\250\213.md" "b/_posts/2023-04-05-stable diffusion\345\205\245\351\227\250\346\225\231\347\250\213.md" new file mode 100644 index 0000000000..fdd2aff16c --- /dev/null +++ "b/_posts/2023-04-05-stable diffusion\345\205\245\351\227\250\346\225\231\347\250\213.md" @@ -0,0 +1,99 @@ +--- +layout: post +title: stable diffusion入门教程 +date: 2023-04-05 +description: "stable diffusion入门教程" +tag: 工具箱 +--- +## 1. 什么是stable diffusion? + +## 2. 环境搭建 + +[python3.10.6](https://www.python.org/ftp/python/3.10.6/python-3.10.6-amd64.exe) + +也可以从python release页面下载: +[https://www.python.org/downloads/windows/](https://www.python.org/downloads/windows/) + +项目地址: +[stable diffusion](https://github.com/AUTOMATIC1111/stable-diffusion-webui) + +拉取到本地: + +```bash +https://github.com/AUTOMATIC1111/stable-diffusion-webui.git +``` + +可能会遇到的问题: + +windows下已经存在了其他的python版本,要先切换默认的python版本: + +```bash +py --version # 查看当前默认的python版本 +py -0p # 查看当前系统中安装的python版本 +py -3.x -m ensurepip --default-pip # 切换默认的python版本 +``` + +或者找到“系统属性”,点击“环境变量”,在“系统变量”中找到“Path”,在“Path”中应该已经有了两个python路径,删除其中一个python路径或者把其中想要的路径上移到最前面即可。 + +如果已经用了其他版本的python,要先去stable-diffusion-webui文件夹下面的venv删除所有文件再进行操作。 + +## 下载stable diffusion + +运行webui-user.bat,第一次会下载所需要的whl和依赖包,可能会比较慢,耐心等待。 + +第一次运行出现的问题: +gfpgan安装失败 + +[解决方案](https://blog.csdn.net/weixin_40735291/article/details/129153398) + +* 无法安装gfpgan的原因是**网络问题,就算已经科学上网,并设置为全局,也无法从github上下载源代码,从而导致install失败。** +* 解决方法是直接到github下载 [GFPGAN](https://github.com/TencentARC/GFPGAN) 代码到本地,并进行 **本地安装** 。 +* 因为stable diffusion会在其根目录创建虚拟python环境venv,因此安装方法与github有所不同。可参考以下方法: + * 从github将GFPGAN的源文件下载到本地,这一步可以使用git clone也可以直接下载zip文件。下载后,解压(如果用git clone就不需要)到 `d:\\stable-diffusion-webui\venv\Scripts`目录下(stable-diffusion-webui是你stable diffusion webui的根目录,这个地址只是我电脑中的,请根据自己放的位置调整)。 + * 打开cmd,cd到 `d:\\stable-diffusion-webui\venv\Scripts\GFPGAN-master`下。 + * 使用命令 `d:\\stable-diffusion-webui\venv\Scripts\python.exe -m pip install basicsr facexlib`安装GFPGAN的依赖。 + * 再使用 `d:\\stable-diffusion-webui\venv\Scripts\python.exe -m pip install -r requirements.txt`安装GFPGAN的依赖。 + * 使用 `d:\\stable-diffusion-webui\venv\Scripts\python.exe setup.py develop`安装GFPGAN。 + +如果文件路径有空格,要加上双引号。 +这里的python.exe -m表示使用python.exe这个文件来执行模块pip,也就是pip.py这个文件。 + +安装open_clip失败 + +[解决方案](https://www.bilibili.com/read/cv22604427/) + +还有其他一些小问题,很好解决的。 + +比如说从github上面拉取Stability-AI +/stablediffusion出现 ``Failed to connect to github.com port 443: Timed out``的问题,可以换国内的镜像源解决。 + +```shell +git clone https://hub.fgit.gq/Stability-AI/stablediffusion.git "D:/software1/stable diffusion/stable-diffusion-webui/repositories/stable-diffusion-stability-ai" +``` + +## 运行stable diffusion + +训练模型时报错: + +``torch.cuda.OutOfMemoryError: CUDA out of memory. Tried to allocate 128.00 MiB (GPU 0; 4.00 GiB total capacity; 2.20 GiB already allocated; 44.64 MiB free; 2.41 GiB reserved in total by PyTorch) If reserved memory is >> allocated memory try setting max_split_size_mb to avoid fragmentation. See documentation for Memory Management and PYTORCH_CUDA_ALLOC_CONF`` + +解决方法: + +1. 降低batch_size +2. 换显卡 +3. repositories\stable-diffusion-stability-ai\scripts\txt2img.py + 1. parser.add_argument的–n_samples的default改为1 + 2. `model.to(device)`后添加 `.half()` 没找到这个在哪 +4. 1 + +## Civitai + +自己训练的照片不太满意,可以用Civitai的模型进行训练。 + +https://civitai.com/models/8109/ulzza + +https://civitai.com/models/6424/chill + +## 其他 + +txt2img只是stable diffusion最简单的功能,还可以img2img等等。 diff --git "a/_posts/2023-04-06-\347\276\216\345\233\275\345\242\203\345\206\205\351\243\216\345\212\233\345\217\221\347\224\265\346\234\272\347\273\204\345\210\206\345\270\203\346\225\260\346\215\256\346\216\242\347\264\242.md" "b/_posts/2023-04-06-\347\276\216\345\233\275\345\242\203\345\206\205\351\243\216\345\212\233\345\217\221\347\224\265\346\234\272\347\273\204\345\210\206\345\270\203\346\225\260\346\215\256\346\216\242\347\264\242.md" new file mode 100644 index 0000000000..fb9c2b072d --- /dev/null +++ "b/_posts/2023-04-06-\347\276\216\345\233\275\345\242\203\345\206\205\351\243\216\345\212\233\345\217\221\347\224\265\346\234\272\347\273\204\345\210\206\345\270\203\346\225\260\346\215\256\346\216\242\347\264\242.md" @@ -0,0 +1,193 @@ +--- +layout: post +title: 美国境内风力发电机组分布数据探索 +date: 2023-04-06 +description: "美国境内风力发电机组分布数据探索" +tag: Python +--- + +## 1. 数据来源 + +[kaggle-Wind Farms](https://www.kaggle.com/datasets/txtrouble/wind-farms) + +## 2. 数据探索 + +```python +import numpy as np # linear algebra +import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv) +import matplotlib.pyplot as plt +from mpl_toolkits.basemap import Basemap + +# Input data files are available in the "../input/" directory. +# For example, running this (by clicking run or pressing Shift+Enter) will list the files in the input directory + +from subprocess import check_output +print(check_output(["ls", "../input"]).decode("utf8")) # ls表示列出当前目录下的文件,check_output表示执行命令,decode表示解码 + +wtfile='../input/windTurbines.csv' +wtdat=pd.read_csv(wtfile) +print(wtdat.head()) +``` +结果如下: + +```console + FID unique_id site_name total_turb \ +0 0 982 unknown Gilliam County 2 +1 1 1065 unknown Gilliam County 2 +2 2 1895 Banner Wind Project 2 +3 3 1897 Banner Wind Project 2 +4 4 2608 unknown Tehachapi Wind Resource Area 1 549 + + on_year year_range on_year_s manufac model type_tower ... \ +0 unknown unknown -99999 unknown unknown unknown ... +1 unknown unknown -99999 unknown unknown unknown ... +2 2008 no 2008 Entegrity EW50 trestle ... +3 2008 no 2008 Entegrity EW50 trestle ... +4 1982_1990 yes 1982 unknown unknown monopole ... + + conf_attr conf_loc WENDI_name EIA_name FAA_jdate FAA_AGL \ +0 0 0 unknown unknown 2013192 124.09 +1 0 0 unknown unknown 2013192 124.09 +2 2 2 Banner Wind Project unknown 2009065 38.72 +3 2 2 Banner Wind Project unknown 2009065 38.72 +4 0 2 unknown unknown -99999 -99999.00 + + FAA_ORS image_name image_year \ +0 41-020577 Bing Maps Aerial unknown +1 41-020578 Bing Maps Aerial unknown +2 02-020064 Bing Maps Aerial unknown +3 02-020070 Bing Maps Aerial unknown +4 unknown NAIP 2012 + + comments +0 FAA lists as Dismantle, google temporal analys... +1 FAA lists as Dismantle, google temporal analys... +2 trestle turbines removed, new monopoles instal... +3 trestle turbines removed, new monopoles instal... +4 + +[5 rows x 32 columns] +``` + +数据描述如下: + +字段名称 解释说明 + +FID 文件编码ID + +unique_id 唯一识别码 + +site_name 地点名称 + +total_turb 发电机数量 + +on_year 服役时间 + +year_range 服役时间区间 + +on_year_s 服役开始时间 + +manufac 制造商 + +model 型号 + +type_tower tower的种类 + +decommiss 是否退役 + +MW_turbine 电机的MW + +total_cpcy 总的cpcy + +total_ht 总的高度 + +tower_h tower的高度 + +blade_l 叶片的长度 + +rotor_dia 电机转子的直径 + +rotor_s_a 电机转子的面积 + +lat_DD 纬度 + +long_DD 经度 + +state 所属的州 + +county 所属的郡 + +conf_attr conf的attr + +conf_loc conf的位置 + +WENDI_name WENDI的名称 + +EIA_name EIA的名称 + +FAA_jdate FAA的注册日期 + +FAA_AGL FAA的AGL + +FAA_ORS FAA的ORS + +image_name 照片名称 + +image_year 照片年份 + +comments 评论 + +## 3. 数据可视化 + +在可视化之前,应该还需要对数据进行预处理,比如去除缺失值,去除重复值等等。 + +```python +#% setup the map +fig,ax=plt.subplots(figsize=(20,10)) + +c1=(-127.6+66.1)/2 +c2=(50.5+23.2)/2 +m = Basemap(resolution='i', # c, l, i, h, f or None + projection='merc', + lat_0=c2, lon_0=c1, + llcrnrlon=-127.6, llcrnrlat= 23.2, urcrnrlon=-66.1, urcrnrlat=50.5) +# lat_0表示地图中心的纬度,lon_0表示地图中心的经度 +# llcrnrlon表示西经,llcrnrlat表示南纬,urcrnrlon表示东经,urcrnrlat表示北纬 + +# draw the map +m.drawmapboundary(fill_color='#46bcec') # 设置地图边界颜色, #46bcec是蓝色 +m.fillcontinents(color='#f2f2f2',lake_color='#46bcec') +# 设置陆地颜色,#f2f2f2是灰色,lake_color是湖泊颜色,#46bcec是蓝色 +m.drawcoastlines(linewidth=0.5) +m.drawcountries(linewidth=0.5) +m.drawstates(linewidth=0.5) +x,y=m(np.array(wtdat['long_DD']),np.array(wtdat['lat_DD'])) + +# plot each turbine with marker size based on machine size + +# Check the number of missing values in 'MW_turbine' +print("Number of missing values in 'MW_turbine':", wtdat['MW_turbine'].isnull().sum()) + + +mx=wtdat['MW_turbine'].max() # 最大的MW,MW_turbine表示电机的MW +for i in range(len(wtdat)): + if wtdat['MW_turbine'].loc[i]>0: + m.plot(x[i],y[i],'o', + color='red', + markersize=wtdat['MW_turbine'].loc[i]/mx*15, + alpha=0.25, + markeredgecolor='none') + else: + m.plot(x[i],y[i], + 'x', + color='brown') # x表示叉号 + +plt.show() +fig.savefig('WindTurbineMap.png',orientation='landscape',dpi=600) +``` + +这段代码的含义是,首先设置地图的中心点,然后绘制地图,最后绘制风力发电机的位置,其中地图上的散点的大小是根据风力发电机的MW来决定的。 + +运行结果如下: + +![20230406091501](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230406091501.png) \ No newline at end of file diff --git "a/_posts/2023-04-06-\351\243\216\351\200\237\351\242\204\346\265\213.md" "b/_posts/2023-04-06-\351\243\216\351\200\237\351\242\204\346\265\213.md" new file mode 100644 index 0000000000..1cf5aec313 --- /dev/null +++ "b/_posts/2023-04-06-\351\243\216\351\200\237\351\242\204\346\265\213.md" @@ -0,0 +1,600 @@ +--- +layout: post +title: 风速预测 +date: 2023-04-06 +description: "风速预测" +tag: Python +--- +## 1. 数据来源 + +[kaggle-Wind Speed Prediction Dataset](https://www.kaggle.com/datasets/fedesoriano/wind-speed-prediction-dataset?resource=download) + +气象学家们面临着高精度和可靠的风速预测的挑战。由于对流暴风引起的严重风灾造成了相当大的损害(如大规模的森林破坏、停电、建筑物/房屋损坏等),因此风速预测是获取先进的严重天气警报的重要任务。本数据集包含了收集不同天气变量(如温度和降水)的气象传感器的响应。 + +该数据集包含了6574个实例的日均响应,来自于一个包含5个天气变量传感器阵列的气象站。该设备位于一个相对空旷的区域内,海拔21米。数据记录时间为1961年1月至1978年12月(17年)。提供了地面真实的日均降水量、最高和最低温度以及草地最低温度。 + +属性信息 + +1. DATE (YYYY-MM-DD) +2. WIND: Average wind speed [knots] +3. IND: First indicator value +4. RAIN: Precipitation Amount (mm) +5. IND.1: Second indicator value +6. T.MAX: Maximum Temperature (°C) +7. IND.2: Third indicator value +8. T.MIN: Minimum Temperature (°C) +9. T.MIN.G: 09utc Grass Minimum Temperature (°C) + +## 2. 数据预处理 + +```python +import numpy as np # linear algebra +import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv) + +import os +for dirname, _, filenames in os.walk('./input'): + for filename in filenames: + print(os.path.join(dirname, filename)) + +import matplotlib.pyplot as plt +%matplotlib inline +import seaborn as sns +import warnings +warnings.filterwarnings(action='ignore') + +import datetime + +from sklearn.metrics import mean_absolute_error + +from sklearn.preprocessing import MinMaxScaler +import tensorflow as tf + +df=pd.read_csv('./input/wind_dataset.csv') + +df.head() +df.info() +df.shape + +# 对日期进行处理 +df['DATE']=pd.to_datetime(df['DATE']) +# extract year, month and day from DATE +df['year']=df['DATE'].dt.year +df['month']=df['DATE'].dt.month +df['day']=df['DATE'].dt.day + +``` + +输出结果如下: + +![20230406160240](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230406160240.png) + +```console +Python 3.8.3 (default, Jul 2 2020, 17:30:36) [MSC v.1916 64 bit (AMD64)] +Type 'copyright', 'credits' or 'license' for more information +IPython 7.34.0 -- An enhanced Interactive Python. Type '?' for help. + +./input\wind_dataset.csv + +RangeIndex: 6574 entries, 0 to 6573 +Data columns (total 9 columns): + # Column Non-Null Count Dtype +--- ------ -------------- ----- + 0 DATE 6574 non-null object + 1 WIND 6574 non-null float64 + 2 IND 6574 non-null int64 + 3 RAIN 6574 non-null float64 + 4 IND.1 6513 non-null float64 + 5 T.MAX 5953 non-null float64 + 6 IND.2 6513 non-null float64 + 7 T.MIN 5900 non-null float64 + 8 T.MIN.G 6214 non-null float64 +dtypes: float64(7), int64(1), object(1) +memory usage: 462.4+ KB +DATE WIND IND RAIN IND.1 T.MAX IND.2 T.MIN T.MIN.G year month day +0 1961-01-01 13.67 0 0.2 0.0 9.5 0.0 3.7 -1.0 1961 1 1 +1 1961-01-02 11.50 0 5.1 0.0 7.2 0.0 4.2 1.1 1961 1 2 +2 1961-01-03 11.25 0 0.4 0.0 5.5 0.0 0.5 -0.5 1961 1 3 +3 1961-01-04 8.63 0 0.2 0.0 5.6 0.0 0.4 -3.2 1961 1 4 +4 1961-01-05 11.92 0 10.4 0.0 7.2 1.0 -1.5 -7.5 1961 1 5 + +RangeIndex: 6574 entries, 0 to 6573 +Data columns (total 12 columns): + # Column Non-Null Count Dtype +--- ------ -------------- ----- + 0 DATE 6574 non-null datetime64[ns] + 1 WIND 6574 non-null float64 + 2 IND 6574 non-null int64 + 3 RAIN 6574 non-null float64 + 4 IND.1 6513 non-null float64 + 5 T.MAX 5953 non-null float64 + 6 IND.2 6513 non-null float64 + 7 T.MIN 5900 non-null float64 + 8 T.MIN.G 6214 non-null float64 + 9 year 6574 non-null int64 + 10 month 6574 non-null int64 + 11 day 6574 non-null int64 +dtypes: datetime64[ns](1), float64(7), int64(4) +memory usage: 616.4 KB +``` + + +![20230406160435](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230406160435.png) + + +查看数据是否有缺失值: + +```python +df.isnull().sum() +``` + +结果如下: + +```console +DATE 0 +WIND 0 +IND 0 +RAIN 0 +IND.1 61 +T.MAX 621 +IND.2 61 +T.MIN 674 +T.MIN.G 360 +year 0 +month 0 +day 0 +dtype: int64 +``` + +```python +# 用backfill (bfill)填充null +df = df.fillna(method='bfill') +``` +然后再使用isnull()函数检查是否还有缺失值。 + +也可以用每一列出现最多的值来填充缺失值: + +```python +colu = ["IND.1","T.MAX" ,"IND.2" ,"T.MIN" ,"T.MIN.G" ] +for i in colu: + print(i) + print(df[i].mode()) # mode() 函数返回众数 + df[i].fillna(df[i].mode()[0],inplace=True) # inplace=True表示在原数据上修改 +``` + +## 3. 数据可视化 + +风速随着时间的变化情况: + +```python +df.plot(x='DATE',y='WIND',figsize=(15,8)) +``` + +输出结果如下: + +![20230406161322](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230406161322.png) + +各个特征值之间的相互关系: + +```python +sns.heatmap(df.corr(),annot=True, cbar=False, cmap='Blues', fmt='.1f') +# cbar=False表示不显示颜色条, cmap='Blues'表示颜色为蓝色, fmt='.1f'表示保留一位小数,annot=True表示显示相关系数 +``` + +输出结果如下: + +![20230406161756](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230406161756.png) + + +画出风速,降雨量,最高温度,最低温度的箱线图: + +```python +fig, axes = plt.subplots(2, 2, figsize=(15, 8)) +sns.boxplot(df['WIND'],ax=axes[0,0]) # Boxplot:'Wind' +# axes[0,0].set_title('Wind') # title +sns.boxplot(df['RAIN'],ax=axes[0,1]) # Boxplot:'Rain' +# axes[0,1].set_title('Rain') # title +sns.boxplot(df['T.MAX'],ax=axes[1,0]) # Boxplot:'Max Temperature' +# axes[1,0].set_title('Max Temperature') # title +sns.boxplot(df['T.MIN'],ax=axes[1,1]) # Boxplot:'Min Temperature' +# axes[1,1].set_title('Min Temperature') # title +``` + + +结果如下: + +![20230406162654](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230406162654.png) + +可以发现,风速平均值在9knots左右,降雨量有很多高的异常值,最高温度平均值在12°左右,最低温度平均值在8°左右,最低温度有一些异常的低值。 + + +画出所有变量的时间序列图: + +```python +df = df.set_index('DATE') +df.head() +from matplotlib import pyplot +# load dataset +values = df.values +# specify columns to plot +groups = [0, 1, 2, 3,4, 5, 6, 7] +i = 1 +# plot each column +pyplot.figure(figsize=(15,15)) +for group in groups: + pyplot.subplot(len(groups), 1, i) + pyplot.plot(values[:, group]) + pyplot.title(df.columns[group], y=0.5, loc='right') + i += 1 +pyplot.show() +``` + +结果如下: + +![时间序列图](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/时间序列图.png) + +## 4. 变量之间的关系 + +### 4.1 风速与降雨量的关系 + +```python +sns.scatterplot(x='WIND',y='RAIN',data=df) +``` + +输出结果如下: + +![20230406164723](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230406164723.png) + +### 4.2 降雨量月度变化 + +```python +sns.boxplot(x=df['month'] ,y=df["WIND"]) +``` + +输出结果如下: + + +![20230406164941](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230406164941.png) + +### 4.3 降雨量年度变化折线图 + +```python +sns.lineplot(x=df['year'] ,y=df["RAIN"]) +# sns.lineplot(x=df['year'] ,y=df["RAIN"],ci=None) +# 不要误差线,ci表示置信区间 +# 横坐标为年份,纵坐标为降雨量 +plt.title('Rainfall per year') +plt.xlabel('Year') +plt.ylabel('Rainfall') +# 设置横坐标取值 +plt.xticks(np.arange(1960,1980,2)) +``` + +输出结果如下: + +![20230406165652](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230406165652.png) + + +## 5. 时间序列预测 + +### 5.1 LSTM + +LSTM是指长短期记忆网络(Long Short-Term Memory networks),是一种特殊的RNN,能够学习长期依赖关系。 + +### 5.2 训练 + +这个还不是很懂,还需要时间消化消化。 + +```python +# Train – Test Split +# 去掉年月日的列 +df = df.drop(['year','month','day'],axis=1) +train_df,test_df = df[1:4601], df[4601:] + +# 缩放 +train = train_df +scalers={} +for i in train_df.columns: + scaler = MinMaxScaler(feature_range=(-1,1)) # MinMaxScaler缩放到[-1,1]之间 + s_s = scaler.fit_transform(train[i].values.reshape(-1,1)) + s_s=np.reshape(s_s,len(s_s)) + scalers['scaler_'+ i] = scaler + train[i]=s_s +test = test_df +for i in train_df.columns: + scaler = scalers['scaler_'+i] + s_s = scaler.transform(test[i].values.reshape(-1,1)) + s_s=np.reshape(s_s,len(s_s)) + scalers['scaler_'+i] = scaler + test[i]=s_s + +# 使用滑动窗口方法将我们的序列转换为输入过去观测值的样本, +# 并输出未来观测值,以使用监督式学习算法。 + +def split_series(series, n_past, n_future): + + X, y = list(), list() + for window_start in range(len(series)): + past_end = window_start + n_past + future_end = past_end + n_future + if future_end > len(series): + break + past, future = series[window_start:past_end, :], series[past_end:future_end, :] + X.append(past) + y.append(future) + return np.array(X), np.array(y) + +# 假设根据过去10天的观测,我们需要预测未来5天的观测。 +n_past = 10 +n_future = 5 +n_features = 8 + +# 转换数据成为samples +X_train, y_train = split_series(train.values,n_past, n_future) +X_train = X_train.reshape((X_train.shape[0], X_train.shape[1],n_features)) +y_train = y_train.reshape((y_train.shape[0], y_train.shape[1], n_features)) +X_test, y_test = split_series(test.values,n_past, n_future) +X_test = X_test.reshape((X_test.shape[0], X_test.shape[1],n_features)) +y_test = y_test.reshape((y_test.shape[0], y_test.shape[1], n_features)) + +# 建模 +## E1D1 +encoder_inputs = tf.keras.layers.Input(shape=(n_past, n_features)) +encoder_l1 = tf.keras.layers.LSTM(100, return_state=True) +encoder_outputs1 = encoder_l1(encoder_inputs) + +encoder_states1 = encoder_outputs1[1:] + +decoder_inputs = tf.keras.layers.RepeatVector(n_future)(encoder_outputs1[0]) + +decoder_l1 = tf.keras.layers.LSTM(100, return_sequences=True)(decoder_inputs,initial_state = encoder_states1) +decoder_outputs1 = tf.keras.layers.TimeDistributed(tf.keras.layers.Dense(n_features))(decoder_l1) + +model_e1d1 = tf.keras.models.Model(encoder_inputs,decoder_outputs1) +model_e1d1.summary() + +## E2D2 +## 具有两个编码层和两个解码层的序列模型 +encoder_inputs = tf.keras.layers.Input(shape=(n_past, n_features)) +encoder_l1 = tf.keras.layers.LSTM(100,return_sequences = True, return_state=True) +encoder_outputs1 = encoder_l1(encoder_inputs) +encoder_states1 = encoder_outputs1[1:] +encoder_l2 = tf.keras.layers.LSTM(100, return_state=True) +encoder_outputs2 = encoder_l2(encoder_outputs1[0]) +encoder_states2 = encoder_outputs2[1:] + +decoder_inputs = tf.keras.layers.RepeatVector(n_future)(encoder_outputs2[0]) + +decoder_l1 = tf.keras.layers.LSTM(100, return_sequences=True)(decoder_inputs,initial_state = encoder_states1) +decoder_l2 = tf.keras.layers.LSTM(100, return_sequences=True)(decoder_l1,initial_state = encoder_states2) +decoder_outputs2 = tf.keras.layers.TimeDistributed(tf.keras.layers.Dense(n_features))(decoder_l2) + +model_e2d2 = tf.keras.models.Model(encoder_inputs,decoder_outputs2) + +model_e2d2.summary() + +# 训练模型 +reduce_lr = tf.keras.callbacks.LearningRateScheduler(lambda x: 1e-3 * 0.90 ** x) +model_e1d1.compile(optimizer=tf.keras.optimizers.Adam(), loss=tf.keras.losses.Huber()) +history_e1d1=model_e1d1.fit(X_train,y_train,epochs=25,validation_data=(X_test,y_test),batch_size=32,verbose=0,callbacks=[reduce_lr]) +model_e2d2.compile(optimizer=tf.keras.optimizers.Adam(), loss=tf.keras.losses.Huber()) +history_e2d2=model_e2d2.fit(X_train,y_train,epochs=25,validation_data=(X_test,y_test),batch_size=32,verbose=0,callbacks=[reduce_lr]) + +# 得到预测值 +pred_e1d1=model_e1d1.predict(X_test) +pred_e2d2=model_e2d2.predict(X_test) + +# 反缩放 +for index,i in enumerate(train_df.columns): + scaler = scalers['scaler_'+i] + pred_e1d1[:,:,index]=scaler.inverse_transform(pred_e1d1[:,:,index]) + pred_e2d2[:,:,index]=scaler.inverse_transform(pred_e2d2[:,:,index]) + y_train[:,:,index]=scaler.inverse_transform(y_train[:,:,index]) + y_test[:,:,index]=scaler.inverse_transform(y_test[:,:,index]) + +# check error +from sklearn.metrics import mean_absolute_error +for index,i in enumerate(train_df.columns): + print(i) + for j in range(1,6): + print("Day ",j,":") + print("MAE-E1D1 : ",mean_absolute_error(y_test[:,j-1,index],pred_e1d1[:,j-1,index]),end=", ") + print("MAE-E2D2 : ",mean_absolute_error(y_test[:,j-1,index],pred_e2d2[:,j-1,index])) + print() + print() + +# 评估模型 +def evaluate_model(y_true,y_pred): + mae = np.mean(np.abs(y_true-y_pred)) + mape = np.mean(np.abs((y_true-y_pred)/y_true)) + rmse = np.sqrt(np.mean((y_true-y_pred)**2)) + return mae,mape,rmse + +# MAE表示预测值与真实值之间的平均绝对误差,MAPE表示预测值与真实值之间的平均绝对百分比误差,RMSE表示预测值与真实值之间的均方根误差。 +mae_e1d1,mape_e1d1,rmse_e1d1=evaluate_model(y_test,pred_e1d1) +print("MAE-E1D1 : ",mae_e1d1) +print("MAPE-E1D1 : ",mape_e1d1) +print("RMSE-E1D1 : ",rmse_e1d1) +mae_e2d2,mape_e2d2,rmse_e2d2=evaluate_model(y_test,pred_e2d2) +print("MAE-E2D2 : ",mae_e2d2) +print("MAPE-E2D2 : ",mape_e2d2) +print("RMSE-E2D2 : ",rmse_e2d2) +``` + +输出结果如下: + +```console +Model: "model" +__________________________________________________________________________________________________ + Layer (type) Output Shape Param # Connected to +================================================================================================== + input_1 (InputLayer) [(None, 10, 8)] 0 [] + + lstm (LSTM) [(None, 100), 43600 ['input_1[0][0]'] + (None, 100), + (None, 100)] + + repeat_vector (RepeatVector) (None, 5, 100) 0 ['lstm[0][0]'] + + lstm_1 (LSTM) (None, 5, 100) 80400 ['repeat_vector[0][0]', + 'lstm[0][1]', + 'lstm[0][2]'] + + time_distributed (TimeDistribu (None, 5, 8) 808 ['lstm_1[0][0]'] + ted) + +================================================================================================== +Total params: 124,808 +Trainable params: 124,808 +Non-trainable params: 0 +__________________________________________________________________________________________________ + + +Model: "model_1" +__________________________________________________________________________________________________ + Layer (type) Output Shape Param # Connected to +================================================================================================== + input_2 (InputLayer) [(None, 10, 8)] 0 [] + + lstm_2 (LSTM) [(None, 10, 100), 43600 ['input_2[0][0]'] + (None, 100), + (None, 100)] + + lstm_3 (LSTM) [(None, 100), 80400 ['lstm_2[0][0]'] + (None, 100), + (None, 100)] + + repeat_vector_1 (RepeatVector) (None, 5, 100) 0 ['lstm_3[0][0]'] + + lstm_4 (LSTM) (None, 5, 100) 80400 ['repeat_vector_1[0][0]', + 'lstm_2[0][1]', + 'lstm_2[0][2]'] + + lstm_5 (LSTM) (None, 5, 100) 80400 ['lstm_4[0][0]', + 'lstm_3[0][1]', + 'lstm_3[0][2]'] + + time_distributed_1 (TimeDistri (None, 5, 8) 808 ['lstm_5[0][0]'] + buted) + +================================================================================================== +Total params: 285,608 +Trainable params: 285,608 +Non-trainable params: 0 +__________________________________________________________________________________________________ + + +62/62 [==============================] - 1s 4ms/step +62/62 [==============================] - 2s 7ms/step + +WIND +Day 1 : +MAE-E1D1 : 0.2030982378059999, MAE-E2D2 : 0.20359981017286832 +Day 2 : +MAE-E1D1 : 0.23322182065839472, MAE-E2D2 : 0.2322014858929193 +Day 3 : +MAE-E1D1 : 0.240528954285563, MAE-E2D2 : 0.2399887921500838 +Day 4 : +MAE-E1D1 : 0.24262299357094172, MAE-E2D2 : 0.24300205558516846 +Day 5 : +MAE-E1D1 : 0.24477795444422737, MAE-E2D2 : 0.24521872620795385 + + +IND +Day 1 : +MAE-E1D1 : 0.8192329511403916, MAE-E2D2 : 0.8078464830086023 +Day 2 : +MAE-E1D1 : 0.8205593273674974, MAE-E2D2 : 0.809087825709913 +Day 3 : +MAE-E1D1 : 0.8095268123061028, MAE-E2D2 : 0.7957327934233981 +Day 4 : +MAE-E1D1 : 0.8005428497982366, MAE-E2D2 : 0.7832877624393909 +Day 5 : +MAE-E1D1 : 0.7966770311592184, MAE-E2D2 : 0.7773387461307403 + + +RAIN +Day 1 : +MAE-E1D1 : 0.09180004186695194, MAE-E2D2 : 0.09083399148450579 +Day 2 : +MAE-E1D1 : 0.0924991551961152, MAE-E2D2 : 0.09287003019686486 +Day 3 : +MAE-E1D1 : 0.09169342586566709, MAE-E2D2 : 0.09171447431362073 +Day 4 : +MAE-E1D1 : 0.0918647467209567, MAE-E2D2 : 0.09168521043800822 +Day 5 : +MAE-E1D1 : 0.09274870878211129, MAE-E2D2 : 0.09302511114610458 + + +IND.1 +Day 1 : +MAE-E1D1 : 0.019823942188936086, MAE-E2D2 : 0.022393431063815607 +Day 2 : +MAE-E1D1 : 0.019041566405447238, MAE-E2D2 : 0.021436605376209027 +Day 3 : +MAE-E1D1 : 0.020100323505216622, MAE-E2D2 : 0.02054175316161681 +Day 4 : +MAE-E1D1 : 0.02028246833968978, MAE-E2D2 : 0.019219360192129963 +Day 5 : +MAE-E1D1 : 0.020312563383560998, MAE-E2D2 : 0.01931783006226548 + + +T.MAX +Day 1 : +MAE-E1D1 : 0.1232421095326075, MAE-E2D2 : 0.12522745991132936 +Day 2 : +MAE-E1D1 : 0.1474737389383158, MAE-E2D2 : 0.1490627993920697 +Day 3 : +MAE-E1D1 : 0.15721180848764904, MAE-E2D2 : 0.15875159289268734 +Day 4 : +MAE-E1D1 : 0.16539402954977084, MAE-E2D2 : 0.16673676071107582 +Day 5 : +MAE-E1D1 : 0.16949716272784246, MAE-E2D2 : 0.17063818147683252 + + +IND.2 +Day 1 : +MAE-E1D1 : 0.0710885461311428, MAE-E2D2 : 0.07079118672775456 +Day 2 : +MAE-E1D1 : 0.07985194428287154, MAE-E2D2 : 0.07748176526390453 +Day 3 : +MAE-E1D1 : 0.0854636775923968, MAE-E2D2 : 0.08299859596065504 +Day 4 : +MAE-E1D1 : 0.0882332985987768, MAE-E2D2 : 0.08443187568916721 +Day 5 : +MAE-E1D1 : 0.09076995515531273, MAE-E2D2 : 0.08585589801614049 + + +T.MIN +Day 1 : +MAE-E1D1 : 0.12947372942288168, MAE-E2D2 : 0.13142280593030686 +Day 2 : +MAE-E1D1 : 0.15197962835745546, MAE-E2D2 : 0.15341848565558663 +Day 3 : +MAE-E1D1 : 0.162795914776204, MAE-E2D2 : 0.16349701241098136 +Day 4 : +MAE-E1D1 : 0.16716887079652246, MAE-E2D2 : 0.16748711679387449 +Day 5 : +MAE-E1D1 : 0.17005420679692593, MAE-E2D2 : 0.17034509361844594 + + +T.MIN.G +Day 1 : +MAE-E1D1 : 0.20059115168468492, MAE-E2D2 : 0.19822965409102764 +Day 2 : +MAE-E1D1 : 0.22191297463551657, MAE-E2D2 : 0.22105030430725828 +Day 3 : +MAE-E1D1 : 0.22813145943950772, MAE-E2D2 : 0.2277492541219974 +Day 4 : +MAE-E1D1 : 0.23071047852604046, MAE-E2D2 : 0.2301128151892642 +Day 5 : +MAE-E1D1 : 0.2318879832054546, MAE-E2D2 : 0.23180336898038414 + + +MAE-E1D1 : 0.2210972135857277 +MAPE-E1D1 : inf +RMSE-E1D1 : 0.5905665027284146 +MAE-E2D2 : 0.21918585763242307 +MAPE-E2D2 : inf +RMSE-E2D2 : 0.5937227770902509 +``` + diff --git "a/_posts/2023-04-07-\344\275\277\347\224\250codecursor\350\207\252\345\212\250\347\224\237\346\210\220\344\270\200\344\270\252\350\264\252\345\220\203\350\233\207\346\270\270\346\210\217.md" "b/_posts/2023-04-07-\344\275\277\347\224\250codecursor\350\207\252\345\212\250\347\224\237\346\210\220\344\270\200\344\270\252\350\264\252\345\220\203\350\233\207\346\270\270\346\210\217.md" new file mode 100644 index 0000000000..b5db545925 --- /dev/null +++ "b/_posts/2023-04-07-\344\275\277\347\224\250codecursor\350\207\252\345\212\250\347\224\237\346\210\220\344\270\200\344\270\252\350\264\252\345\220\203\350\233\207\346\270\270\346\210\217.md" @@ -0,0 +1,164 @@ +--- +layout: post +title: 使用codecursor自动生成一个贪吃蛇游戏 +date: 2023-04-07 +description: "使用codecursor自动生成一个贪吃蛇游戏" +tag: Python +--- + + +```python +# Here is a basic implementation of the classic game Snake using Python and Pygame library + +# Importing necessary libraries +import pygame +import time +import random + +# Initializing Pygame +pygame.init() + +# Defining colors +white = (255, 255, 255) +yellow = (255, 255, 102) +black = (0, 0, 0) +red = (213, 50, 80) +green = (0, 255, 0) +blue = (50, 153, 213) + +# Setting display size +dis_width = 600 +dis_height = 400 + +# Creating display +dis = pygame.display.set_mode((dis_width, dis_height)) +pygame.display.set_caption('Snake Game') + +# Setting clock +clock = pygame.time.Clock() + +# Setting snake block size +snake_block = 10 + +# Setting font style +font_style = pygame.font.SysFont(None, 30) + + +def our_snake(snake_block, snake_List): + """ + Function to create snake + """ + for x in snake_List: + pygame.draw.rect(dis, black, [x[0], x[1], snake_block, snake_block]) + + +def message(msg, color): + """ + Function to display message + """ + mesg = font_style.render(msg, True, color) + dis.blit(mesg, [dis_width / 6, dis_height / 3]) + + +def gameLoop(): + """ + Main game function + """ + game_over = False + game_close = False + + # Setting starting position of snake + x1 = dis_width / 2 + y1 = dis_height / 2 + + # Setting change in position of snake + x1_change = 0 + y1_change = 0 + + # Creating initial snake + snake_List = [] + Length_of_snake = 1 + + # Generating food at random position + foodx = round(random.randrange(0, dis_width - snake_block) / 10.0) * 10.0 + foody = round(random.randrange(0, dis_height - snake_block) / 10.0) * 10.0 + + # Main game loop + while not game_over: + + # If game is over + while game_close == True: + dis.fill(blue) + message("You Lost", red) + pygame.display.update() + + # Asking user to play again or quit + for event in pygame.event.get(): + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_q: + game_over = True + game_close = False + if event.key == pygame.K_c: + gameLoop() + + # Changing position of snake + for event in pygame.event.get(): + if event.type == pygame.QUIT: + game_over = True + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_LEFT: + x1_change = -snake_block + y1_change = 0 + elif event.key == pygame.K_RIGHT: + x1_change = snake_block + y1_change = 0 + elif event.key == pygame.K_UP: + y1_change = -snake_block + x1_change = 0 + elif event.key == pygame.K_DOWN: + y1_change = snake_block + x1_change = 0 + + # If snake hits the wall + if x1 >= dis_width or x1 < 0 or y1 >= dis_height or y1 < 0: + game_close = True + + # Changing position of snake + x1 += x1_change + y1 += y1_change + dis.fill(blue) + pygame.draw.rect(dis, green, [foodx, foody, snake_block, snake_block]) + + # Updating snake + snake_Head = [] + snake_Head.append(x1) + snake_Head.append(y1) + snake_List.append(snake_Head) + if len(snake_List) > Length_of_snake: + del snake_List[0] + + for x in snake_List[:-1]: + if x == snake_Head: + game_close = True + + our_snake(snake_block, snake_List) + pygame.draw.rect(dis, green, [foodx, foody, snake_block, snake_block]) + pygame.display.update() + + # If snake hits the food + if x1 == foodx and y1 == foody: + foodx = round(random.randrange(0, dis_width - snake_block) / 10.0) * 10.0 + foody = round(random.randrange(0, dis_height - snake_block) / 10.0) * 10.0 + Length_of_snake += 1 + clock.tick(20) + + # Deactivating Pygame library + pygame.quit() + + # Quitting program + quit() + + +if __name__ == '__main__': + gameLoop() +``` \ No newline at end of file diff --git "a/_posts/2023-04-07-\344\275\277\347\224\250stable diffusion\345\222\214playgroundAI\345\257\273\346\211\276WebUI\350\256\276\350\256\241\347\232\204\347\201\265\346\204\237.md" "b/_posts/2023-04-07-\344\275\277\347\224\250stable diffusion\345\222\214playgroundAI\345\257\273\346\211\276WebUI\350\256\276\350\256\241\347\232\204\347\201\265\346\204\237.md" new file mode 100644 index 0000000000..6795f72d0f --- /dev/null +++ "b/_posts/2023-04-07-\344\275\277\347\224\250stable diffusion\345\222\214playgroundAI\345\257\273\346\211\276WebUI\350\256\276\350\256\241\347\232\204\347\201\265\346\204\237.md" @@ -0,0 +1,19 @@ +--- +layout: post +title: "使用stable diffusion和playgroundAI寻找WebUI设计的灵感" +date: 2023-04-07 +desc: "使用stable diffusion和playgroundAI寻找WebUI设计的灵感" +tags: 工具箱 + +--- +## 从playgroundAI中寻找prompt + +比如说搜索[https://playgroundai.com/search?q=web+ui](https://playgroundai.com/search?q=web+ui) + +然后找到喜欢的模板,复制上面的prompt,然后在stable diffusion中就可以使用同样的Prompt生成了。 + +![20230407082044](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230407082044.png) + +## 其他 + +Seed、Guidance Scale、Sampler、Model、Initial Image Strength这些都会影响生成的图片的质量,可以自己尝试一下。 diff --git "a/_posts/2023-04-08-\345\210\235\351\227\273\344\270\215\347\237\245\346\233\262\344\270\255\346\204\217\357\274\214\345\206\215\351\227\273\345\267\262\346\230\257\346\233\262\344\270\255\344\272\272\357\274\210\347\275\221\346\230\223\351\237\263\344\271\220\347\262\276\351\200\211\350\257\204\350\256\272\346\225\260\346\215\256\345\210\206\346\236\220\357\274\211.md" "b/_posts/2023-04-08-\345\210\235\351\227\273\344\270\215\347\237\245\346\233\262\344\270\255\346\204\217\357\274\214\345\206\215\351\227\273\345\267\262\346\230\257\346\233\262\344\270\255\344\272\272\357\274\210\347\275\221\346\230\223\351\237\263\344\271\220\347\262\276\351\200\211\350\257\204\350\256\272\346\225\260\346\215\256\345\210\206\346\236\220\357\274\211.md" new file mode 100644 index 0000000000..ac878c56b7 --- /dev/null +++ "b/_posts/2023-04-08-\345\210\235\351\227\273\344\270\215\347\237\245\346\233\262\344\270\255\346\204\217\357\274\214\345\206\215\351\227\273\345\267\262\346\230\257\346\233\262\344\270\255\344\272\272\357\274\210\347\275\221\346\230\223\351\237\263\344\271\220\347\262\276\351\200\211\350\257\204\350\256\272\346\225\260\346\215\256\345\210\206\346\236\220\357\274\211.md" @@ -0,0 +1,132 @@ +--- +layout: post +title: "初闻不知曲中意,再闻已是曲中人(网易音乐精选评论数据分析)" +date: 2023-04-08 +description: "初闻不知曲中意,再闻已是曲中人(网易音乐精选评论数据分析)" +tag: Python +--- + +## 1. 数据来源 +[和鲸社区-网易云精彩评论数据集 +](https://www.heywhale.com/mw/dataset/5eb69b4d366f4d002d77d63c/content) + +## 2. 数据预处理 + +```python +import pandas as pd +import jieba +from wordcloud import WordCloud +import matplotlib.pyplot as plt +import jieba.analyse + + + +# read in file with specified column names +df = pd.read_csv('网易云音乐精彩评论.csv', + names=['用户Id', '昵称', '评论内容', '点赞数'], + encoding='gbk') + +``` + +encoding='gbk',因为csv文件是gbk编码的,如果不指定,会乱码。 + +## 3. 数据分析 + +```python +# 分词2 +word_list=[] +for comment in df['评论内容']: + try: + # topK: 返回几个 TF/IDF 权重最大的关键词,默认值为 20 + # withWeight: 如果为 True,返回一个 list,其中每个元素都是 (关键词, 权重) 二元组; + # 如果为 False,返回一个 list,其中每个元素为关键词 + # allowPOS: 仅包括指定词性的词,默认值为空,即不筛选 + tags = jieba.analyse.extract_tags(comment, topK=10, withWeight=False, allowPOS=()) + for t in tags: + word_list.append(t) + except Exception as e: + print(e) + +# jieba.load_userdict('../myDict/myDict.txt') +# convert list to dictionary +word_list = dict(pd.Series(word_list).value_counts()) +``` + +结果如下: +```console +{'首歌': 22, + '评论': 19, + '内容': 12, + '成都': 10, + '大哭': 8, + '我们': 7, + '自己': 7, + '16': 7, + '周杰伦': 7, + '喜欢': 7, + '音乐': 6, + '初恋': 6, + '时候': 6, + '十年': 6, + '单曲': 6, + '网易': 5, + '钟无艳': 5, + '女朋友': 5, + '七里香': 5, + '我要': 4, + '赵雷': 4, + '回忆': 4, + '开口': 4, + '青春': 4, + '考研': 4, +... + '1800': 1, + '五湖四海': 1, + '义无反顾': 1, + '牵挂': 1, + ...} +``` + +## 4. 词云图 + +```python +# 制作图云 +def wordCloud(wordList): + from wordcloud import WordCloud, ImageColorGenerator + import matplotlib.pyplot as plt + import numpy as np + import PIL.Image as Image + import os + d = os.path.dirname(__file__) + img = Image.open(os.path.join(d, "jupiter.png")) + width = img.width/80 + height = img.height/80 + alice_coloring = np.array(img) + my_wordcloud = WordCloud(background_color="white", + max_words=500, mask=alice_coloring, + max_font_size=200, random_state=42, + font_path=(os.path.join(d, "./PingFang.ttc"))) + my_wordcloud = my_wordcloud.generate_from_frequencies(wordList) + + image_colors = ImageColorGenerator(alice_coloring) + plt.figure(figsize=(width, height)) + plt.imshow(my_wordcloud.recolor(color_func=image_colors)) + plt.imshow(my_wordcloud) + plt.axis("off") + # 通过设置subplots_adjust来控制画面外边框 + plt.subplots_adjust(bottom=.01, top=.99, left=.01, right=.99) + plt.savefig("jupiter_wordcloud_1.png",dpi=900) + plt.show() + +wordCloud(word_list) +``` + +![jupiter_wordcloud_1](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/jupiter_wordcloud_1.png) + +![wordcloud_2](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/wordcloud_2.png) + +## 5. 问题 + +* 1. 分词结果不太满意,一些停用词没有去掉。 +* 2. 词云图的字体不太满意,需要自己找一些字体。 +* 3. 分词之前最好导入自己的词典,这样分词效果会更好。 \ No newline at end of file diff --git "a/_posts/2023-04-09-VBA\345\207\275\346\225\260\345\210\235\346\216\242\357\274\210\346\211\271\351\207\217\345\265\214\345\205\245\345\233\276\347\211\207\345\271\266\346\214\207\345\256\232\345\233\276\347\211\207\345\244\247\345\260\217).md" "b/_posts/2023-04-09-VBA\345\207\275\346\225\260\345\210\235\346\216\242\357\274\210\346\211\271\351\207\217\345\265\214\345\205\245\345\233\276\347\211\207\345\271\266\346\214\207\345\256\232\345\233\276\347\211\207\345\244\247\345\260\217).md" new file mode 100644 index 0000000000..db40884fd4 --- /dev/null +++ "b/_posts/2023-04-09-VBA\345\207\275\346\225\260\345\210\235\346\216\242\357\274\210\346\211\271\351\207\217\345\265\214\345\205\245\345\233\276\347\211\207\345\271\266\346\214\207\345\256\232\345\233\276\347\211\207\345\244\247\345\260\217).md" @@ -0,0 +1,44 @@ +--- +layout: post + +title: "VBA函数初探(批量嵌入图片并指定图片大小)" + +date: 2023-04-09 + +description: "VBA函数初探(批量嵌入图片并指定图片大小)" + +tag: VBA +--- +## 准备 + +图片路径下的三张图片,分布命名为01.jpg、02.jpg、03.jpg。 + +A2单元格中输入01,A3单元格中输入02,A4单元格中输入03。 + +想要效果是在F2单元格中插入01.jpg,F3单元格中插入02.jpg,F4单元格中插入03.jpg。且图片大小为150*150。 + +## 代码块 + +```vba +Sub InsertP() + Dim c As Range, i As Integer + For Each c In Range("A2:A4") + Range("F" & c.Row).Select + ActiveSheet.Pictures.Insert("*\图片\" & c.Value & ".jpg").Select + Selection.Placement = xlMoveAndSize + Selection.ShapeRange.Height = 150 + Selection.ShapeRange.Width = 150 + Application.CommandBars("Format Object").Visible = False + Next +End Sub +``` + +Sub是宏的开始,End Sub是宏的结束。Sub后面的InsertP是宏的名称,可以随意取名。在Sub和End Sub之间的代码就是宏的主体。在这个宏中,我们使用了一个For Each循环,循环的范围是A2:A4,也就是说,我们要在A2:A4这三个单元格中的内容分别作为图片的名称,然后在F2:F4这三个单元格中插入图片。 + +Dim c As Range表示定义一个变量c,这个变量的类型是Range。For Each后面的c表示循环中的变量,也就是说,我们在循环中使用的变量是c。For Each后面的Range("A2:A4")表示循环的范围,也就是说,我们要循环A2:A4这三个单元格。 + +Range("F" & c.Row).Select表示选中F2、F3、F4这三个单元格。 + +ActiveSheet.Pictures.Insert("\*\图片\" & c.Value & ".jpg").Select表示在选中的单元格中插入图片,其中*表示当前文件的路径,\图片\表示图片文件夹的路径,c.Value表示A2、A3、A4这三个单元格中的内容,.jpg表示图片的格式。Selection.Placement = xlMoveAndSize表示图片的位置和大小可以随单元格的大小而改变。Selection.ShapeRange.Height = 150表示图片的高度为150。Selection.ShapeRange.Width = 150表示图片的宽度为150。Application.CommandBars("Format Object").Visible = False表示隐藏图片的边框。 + +*换成对应的文件路径。 \ No newline at end of file diff --git "a/_posts/2023-04-12-\351\237\263\344\271\220\344\270\213\350\275\275\345\231\250\351\203\250\347\275\262\346\220\255\345\273\272.md" "b/_posts/2023-04-12-\351\237\263\344\271\220\344\270\213\350\275\275\345\231\250\351\203\250\347\275\262\346\220\255\345\273\272.md" new file mode 100644 index 0000000000..5402373147 --- /dev/null +++ "b/_posts/2023-04-12-\351\237\263\344\271\220\344\270\213\350\275\275\345\231\250\351\203\250\347\275\262\346\220\255\345\273\272.md" @@ -0,0 +1,161 @@ +--- +layout: post +title: "音乐下载器部署搭建" +date: 2023-04-12 +description: "音乐下载器部署搭建" +tag: Python +--- +## 项目地址 + +[https://github.com/CharlesPikachu/musicdl](https://github.com/CharlesPikachu/musicdl) + +项目下载: + +```shell +git clone https://github.com/CharlesPikachu/musicdl.git +``` + +## 项目启动 + +```shell +pip install -r requirements.txt +python setup.py install # cd musicdl +python musicdl.py +``` + +## 项目运行 + +### 在终端运行代码 + +```shell +musicdl -k 起风了 +``` + +结果如下: + +```console +(venv) hugo@hugo-virtual-machine:~/PycharmProjects/musicdl-master/examples/singerlyricsanalysis$ musicdl -k 起风了 +Welcome to use musicdl! +You can visit https://github.com/CharlesPikachu/musicdl for more details. +2023-04-11 15:42:33 INFO 正在搜索 起风了 来自 KUGOU|KUWO|QQMUSIC|QIANQIAN|FIVESING|NETEASE|MIGU|JOOX|YITING +2023-04-11 15:42:36 WARNING 无法在migu中搜索 >>>> 起风了 ++------+----------------+---------------------------------+---------+----------+------------------------------+----------+ +| 序号 | 歌手 | 歌名 | 大小 | 时长 | 专辑 | 来源 | ++------+----------------+---------------------------------+---------+----------+------------------------------+----------+ +| 0 | 周深 | 起风了-BILIBILI 11周年演讲 | -MB | 00:05:11 | 起风了 | KUWO | +| 1 | 买辣椒也用券 | 起风了-原曲《ヤキモチ》—高桥优 | -MB | 00:05:12 | 起风了 (旧版) | KUWO | +| 2 | 林俊杰 | 起风了 | -MB | 00:04:57 | - | KUWO | +| 3 | 周深 | 起风了 | -MB | 00:05:11 | 起风了 | KUWO | +| 4 | 买辣椒也用券 | 起风了 | -MB | 00:05:25 | 起风了 | KUWO | +| 5 | 买辣椒也用券 | 起风了 | 4.97MB | 00:05:25 | 起风了 | KUGOU | +| 6 | 周深 | 起风了 | 4.75MB | 00:05:11 | 起风了 | KUGOU | +| 7 | 林俊杰 | 起风了 (Live) | 4.73MB | 00:05:09 | - | KUGOU | +| 8 | 买辣椒也用券 | 起风了 (旧版) | 4.78MB | 00:05:12 | 起风了 (旧版) | KUGOU | +| 9 | 吴青峰 | 起风了 | 4.79MB | 00:05:13 | 我们的主题曲Vol.2 | KUGOU | +| 10 | 买辣椒也用券 | 起风了 | 4.97MB | 00:05:25 | 起风了 | QQMUSIC | +| 11 | 买辣椒也用券 | 起风了 (旧版) | 4.78MB | 00:05:12 | 起风了 (旧版) | QQMUSIC | +| 12 | 周深 | 起风了 | 4.75MB | 00:05:11 | 起风了 | QQMUSIC | +| 13 | 吴青峰 | 起风了 (Live) | 4.54MB | 00:04:57 | - | QQMUSIC | +| 14 | Vk | 起风了 | 12.09MB | -:-:- | - | FIVESING | +| 15 | 祈小夕 | 起风了 | 12.49MB | -:-:- | - | FIVESING | +| 16 | 萧忆情Alex | 起风了 | 12.36MB | -:-:- | - | FIVESING | +| 17 | Assen捷 | 起风了 | 12.38MB | -:-:- | - | FIVESING | +| 18 | 雪霏岚岚 | 起风了(粤语版) | 11.85MB | -:-:- | - | FIVESING | +| 19 | 买辣椒也用券 | 起风了 | 4.97MB | 00:05:25 | 起风了 | NETEASE | +| 20 | 吴青峰 | 起风了 | 4.79MB | 00:05:13 | 起风了 | NETEASE | +| 21 | 周深 | 起风了 | 4.75MB | 00:05:11 | 起风了 | NETEASE | +| 22 | 五月 | 起风了(旧版) | 12.66MB | 00:05:31 | 暖风 | NETEASE | +| 23 | 田杰 | 起风了(0.89x)降调版 | 5.09MB | 00:02:13 | 0 | NETEASE | +| 24 | 艺宝宝(权艺) | 起风 | 3.0MB | 00:03:16 | 起风 | QIANQIAN | +| 25 | 轻音作家 | 起风(钢琴曲) | 2.75MB | 00:03:01 | 静心午茶古典BGM 经典钢琴小品 | QIANQIAN | +| 26 | 子弹壳乐队 | 起风了你在哪呢 | 5.05MB | 00:05:31 | 起风了你在哪呢 | QIANQIAN | +| 27 | 宋瑞 | 起风了(咚鼓版) | 6.28MB | 00:06:51 | 曲风豪横 | QIANQIAN | +| 28 | 彭子瀛 | 起风后 | 3.09MB | 00:03:22 | 起风后 | QIANQIAN | ++------+----------------+---------------------------------+---------+----------+------------------------------+----------+ +请输入想要下载的音乐编号: + +``` + +输入想要下载的音乐编号即可!!! + +### 通过代码下载 + +这个代码会从target_srcs指定的平台,下载能搜索到的音乐(每个平台显示前五个结果),并保存到savedir指定的目录下。 + +savedir可以通过修改在musicdl文件夹中的config.json文件进行修改。 + +```python +from musicdl import musicdl + +config = {'logfilepath': 'musicdl.log', 'savedir': 'downloaded', 'search_size_per_source': 5, 'proxies': {}} +target_srcs = [ + 'kugou', 'kuwo', 'qqmusic', 'qianqian', 'fivesing', + 'netease', 'migu', 'joox', 'yiting', +] + +# target_srcs是一个列表对象, 用于自定义平台: +# lizhi: 荔枝FM +# migu: 咪咕音乐 +# kuwo: 酷我音乐 +# joox: JOOX音乐 +# kugou: 酷狗音乐 +# yiting: 一听音乐 +# qqmusic: QQ音乐 +# qianqian: 千千音乐 +# fivesing: 5SING音乐 +# netease: 网易云音乐 +# ximalaya: 喜马拉雅 + +client = musicdl.musicdl(config=config) +search_results = client.search('起风了', target_srcs) +print(search_results) +for key, value in search_results.items(): + client.download(value) + +``` + +## 延伸功能 + +### 音乐下载器GUI + +使用PyQt5编写的GUI,可以直接在GUI中搜索并下载音乐。 + +```shell +cd examples/musicdlgui +python musicdlgui.py +``` + +下载页面如下,右键选择想要下载的音乐即可下载: + +![20230411155122](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230411155122.png) + +### 歌词获取歌曲片段 + +```shell +cd examples/searchlyrics +python searchlyrics.py +``` + +结果示意: + +![20230411155709](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230411155709.png) + +### 歌手歌词情感分析和词云图 + +```shell +cd examples/singerlyricsanalysis +python singerlyricsanalysis.py +``` + +结果如下: + +![20230411160105](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230411160105.png) + +## 打包成EXE文件 + +使用PyInstaller打包成EXE文件,可以直接在Windows系统中运行。 + +```shell +pip install pyinstaller +pyinstaller --onefile --icon=icon2.ico --name=音乐小站 musicdlgui.py +``` diff --git "a/_posts/2023-04-13-\347\231\276\345\272\246\347\277\273\350\257\221\346\216\245\345\217\243\350\260\203\347\224\250.md" "b/_posts/2023-04-13-\347\231\276\345\272\246\347\277\273\350\257\221\346\216\245\345\217\243\350\260\203\347\224\250.md" new file mode 100644 index 0000000000..f3ed2e6261 --- /dev/null +++ "b/_posts/2023-04-13-\347\231\276\345\272\246\347\277\273\350\257\221\346\216\245\345\217\243\350\260\203\347\224\250.md" @@ -0,0 +1,85 @@ +--- +layout: post +title: "百度翻译接口调用" +date: 2023-04-13 +description: "百度翻译接口调用" +tag: Python +--- + + +## 一、百度开发者平台账号申请 + +先要申请一个开发者账号,然后获取appid和secretKey,这两个参数是调用百度翻译接口的必要参数。 + +地址:[开发者中心信息](http://api.fanyi.baidu.com/manage/developer) +## 二、百度翻译接口调用 + +```python +import requests +import random +import hashlib +import urllib +import json + + +class BaiduTranslate(object): + def __init__(self, word): + # 你要翻译的元素 + self.q = word + self.fromLang = 'en' + self.toLang = 'zh' + self.baidu_translate = 'https://api.fanyi.baidu.com' + self.translate_api_url = '/api/trans/vip/translate' + + # 百度开发者配置信息 + self.appid = '***' + self.secretKey = '***' + + # 开发配置 + self.salt = random.randint(32768, 65536) + self.sign = self.appid + self.q + str(self.salt) + self.secretKey + m1 = hashlib.md5() + m1.update(self.sign.encode('utf-8')) + self.sign = m1.hexdigest() + self.my_url = self.translate_api_url + '?appid=' + self.appid + '&q=' + \ + urllib.request.quote(self.q) +\ + '&from=' + self.fromLang + '&to=' + self.toLang +\ + '&salt=' + str(self.salt) + '&sign=' + self.sign + + def en_translate_zh(self): + re = requests.request('post', self.baidu_translate + self.my_url) + print('\n\t re.text', re.text) + re_json = json.loads(re.text) + print('\n\t re_json', re_json) + print('\n\t',re_json['trans_result'][0]['dst']) # [0]表示第一个翻译结果,dst表示翻译结果 + return re_json['trans_result'][0]['dst'] + +def readfromword(filename): + filename = filename + '.txt' + with open(filename, 'r') as f: + # realines()方法读取所有行,返回一个列表 + lines = f.readlines() + # 拼接列表成为一个字符串 + word = ''.join(lines) + # word中去掉/n换行符 + word = word.replace('\n', ' ') + print(word) + return word + + +# bt.en_translate_zh()结果输出到txt文件 +def write2txt(filename, word): + filename = filename + '.txt' + with open(filename, 'w') as f: + f.write(word) + + +if __name__ == "__main__": + filename= 'test' + test=readfromword(filename) + bt = BaiduTranslate(test) + bt.en_translate_zh() + write2txt('output', str(bt.en_translate_zh())) +``` + +这个代码主要实现了以下功能,首先readfromword()函数读取txt文件,然后将txt文件中的内容传入BaiduTranslate()类中,然后调用en_translate_zh()函数,得到翻译结果,使用write2txt函数将翻译结果输出到txt文件中。 \ No newline at end of file diff --git "a/_posts/2023-04-13-\347\272\277\347\250\213\345\210\235\346\216\242.md" "b/_posts/2023-04-13-\347\272\277\347\250\213\345\210\235\346\216\242.md" new file mode 100644 index 0000000000..0dcb349dc1 --- /dev/null +++ "b/_posts/2023-04-13-\347\272\277\347\250\213\345\210\235\346\216\242.md" @@ -0,0 +1,136 @@ +--- +layout: post +title: "线程初探" +date: 2023-04-13 +description: "线程初探" +tag: Python +--- + +## 一、单线程和多线程比较 + +单线程代码如下: + +```python +import threading +from datetime import * + + +def test(): + print (datetime.now()) + + +def thd(): + Threads = [] + # 创建线程 + for i in range(500): + t = threading.Thread(target=test) + Threads.append(t) + + # 启动线程 + for t in Threads: + t.start() + # 阻塞线程 + for t in Threads: + t.join() + + + +if __name__ == "__main__": + start_time = datetime.now() + thd() + end_time = datetime.now() + print("执行总共耗时:", end_time - start_time, "秒") +``` + +注意,这里还用了一个t.join(),这个方法的作用是阻塞线程,这是为了保证所有的子线程都执行完毕后,再执行主线程的代码。 + +多线程代码如下: + +```python +import threading +from datetime import * + + +def test(): + print (datetime.now()) + + +def looptest(): + for i in range(20): + test() + + +def thd(): + Threads = [] + + # 创建线程 + for i in range(25): + t = threading.Thread(target=looptest) + Threads.append(t) + for t in Threads: + t.start() + # 阻塞线程 + for t in Threads: + t.join() + + +if __name__ == "__main__": + start_time = datetime.now() + thd() + end_time = datetime.now() + print("执行总共耗时:", end_time - start_time, "秒") +``` + +原来的代码,需要并发执行500次test()函数,启动了500个线程,速度比较慢,非常消耗资源;新的代码,把500的并发拆成25个线程,每个线程并发执行20次test()函数,速度明显提升。 + +结果如下: + +```console +# 单线程 +执行总共耗时: 0:00:00.078003 秒 +# 多线程执行 +执行总共耗时: 0:00:00.013262 秒 +``` + +## 二、守护线程 + +在理解守护线程之前先要知道主线程和子线程的区别,第一节创建的线程只是main()线程的子线程,即先启动主线程main(),然后执行thd()启动 +子线程。 + +守护线程是指在主线程结束后,不管守护线程(子线程)是否执行完毕,都会强制结束守护线程。 + +我们用一个死循环来测试一下,添加t.setDaemon(True)就是守护线程。 + +如果不添加这一行代码,当主线程执行完毕打印了主线程结束,然后程序继续等待子线程执行完毕,但是子线程是一个死循环,所以程序会一直等待下去,无法终止程序了。 + +```python +import threading +from datetime import * +from time import sleep + +def test(): + + sleep(1) + + x = 0 + while(x == 0): + print (datetime.now()) + +def thd(): + Threads = [] + # 创建线程 + for i in range(500): + t = threading.Thread(target=test) + Threads.append(t) + # 守护线程 + t.setDaemon(True) + + # 启动线程 + for t in Threads: + t.start() + + +if __name__ == "__main__": + thd() + print("主线程结束") +``` \ No newline at end of file diff --git "a/_posts/2023-04-14-\351\230\264\345\244\251\357\274\214\351\233\250\345\244\251\357\274\214\346\231\264\345\244\251\357\274\214\344\275\240\345\226\234\346\254\242\345\223\252\344\270\200\344\270\252\357\274\210\344\275\277\347\224\250\347\245\236\347\273\217\347\275\221\347\273\234\350\277\233\350\241\214\346\250\241\345\236\213\350\256\255\347\273\203\345\271\266\350\277\233\350\241\214\345\244\251\346\260\224\345\233\276\350\257\206\345\210\253\345\222\214\345\210\206\347\261\273\357\274\211.md" "b/_posts/2023-04-14-\351\230\264\345\244\251\357\274\214\351\233\250\345\244\251\357\274\214\346\231\264\345\244\251\357\274\214\344\275\240\345\226\234\346\254\242\345\223\252\344\270\200\344\270\252\357\274\210\344\275\277\347\224\250\347\245\236\347\273\217\347\275\221\347\273\234\350\277\233\350\241\214\346\250\241\345\236\213\350\256\255\347\273\203\345\271\266\350\277\233\350\241\214\345\244\251\346\260\224\345\233\276\350\257\206\345\210\253\345\222\214\345\210\206\347\261\273\357\274\211.md" new file mode 100644 index 0000000000..de0ca3cb0f --- /dev/null +++ "b/_posts/2023-04-14-\351\230\264\345\244\251\357\274\214\351\233\250\345\244\251\357\274\214\346\231\264\345\244\251\357\274\214\344\275\240\345\226\234\346\254\242\345\223\252\344\270\200\344\270\252\357\274\210\344\275\277\347\224\250\347\245\236\347\273\217\347\275\221\347\273\234\350\277\233\350\241\214\346\250\241\345\236\213\350\256\255\347\273\203\345\271\266\350\277\233\350\241\214\345\244\251\346\260\224\345\233\276\350\257\206\345\210\253\345\222\214\345\210\206\347\261\273\357\274\211.md" @@ -0,0 +1,385 @@ +--- +layout: post +title: "阴天,雨天,晴天,你喜欢哪一个(使用神经网络进行模型训练并进行天气图识别和分类)" +date: 2023-04-14 +description: "阴天,雨天,晴天,你喜欢哪一个(使用神经网络进行模型训练并进行天气图识别和分类)" +tag: Python + +--- + +## 一、项目目录结构和启动流程 + +目录结构如下: + +```console +├─cloudy +├─lin_predict +├─lin_test +│ ├─_cloudy_ +│ ├─_haze_ +│ ├─_rainy_ +│ ├─_snow_ +│ ├─_sunny_ +│ └─_thunder_ +├─lin_train +│ ├─_cloudy +│ ├─_haze +│ ├─_rainy +│ ├─_snow +│ ├─_sunny +│ └─_thunder +├─rainy +├─haze +└─snow +└─sunny +└─thunder +└─re_name.py +└─resize.py +└─split_test_train.py +└─img_label_and_model.py +└─prediction.py +└─requirements.txt +``` + +在snow,cloudy,rainy,sunny,thunder,haze文件夹中分别存放了对应的10000张天气图,这里我使用了6种天气图。 + +第一步,使用re_name.py对图片进行重命名,将图片命名成0.jpg到9999.jpg。 + +第二步,使用resize.py对图片进行缩放,比如说将图片缩放成100像素*100像素。16G内存的电脑,像素大小为220*220大概是训练的极限了。 + +第三步,使用split_test_train.py将图片分成训练集和测试集,比如说从10000张图片中随机抽取500张作为训练集,100张作为测试集,分别保存在lin_train和lin_test文件夹下面对应的天气文件夹中。 + +第四步,开始训练模型,使用img_label_and_model.py,这个文件中包含了图片的预处理,图片的标签化,模型的训练,模型的保存,模型的评估等等。 + +第五步,使用prediction.py对模型进行预测,这里需要预测的图片文件放在lin_predict文件夹下面。 + + +## 二、代码解析 + +### 1、re_name.py + +这个代码要运行6次,分别对应6种天气图的重命名。 + +```python +import os +class BatchRename(): + ''' + 批量重命名文件夹中的图片文件 + ''' + def __init__(self): + # 此处应该为自己存放图片文件夹的路径 + # self.path = "./cloudy" + # self.path = "./haze" + # self.path = "./rainy" + # self.path = "./snow" + # self.path = "./sunny" + self.path = "./thunder" + + def rename(self): + filelist = os.listdir(self.path) + total_num = len(filelist) + i = 0 + for item in filelist: + if item.endswith('.jpg'): + src = os.path.join(os.path.abspath(self.path), item) # 原图的文件名 + dst = os.path.join(os.path.abspath(self.path), str(i) + '.jpg') # 新图的文件名字 + try: + os.rename(src, dst) + print('converting %s to %s ...' % (src, dst)) + i = i + 1 + except: + continue + print ('total %d to rename & converted %d jpgs' % (total_num, i)) + +if __name__ == '__main__': + demo = BatchRename() + demo.rename() +``` + +### 2、resize.py + +```python +# 1.数据清洗 +import os +from PIL import Image + + +def read_image(paths): + os.listdir(paths) + filelist = [] + for root, dirs, files in os.walk(paths): + for file in files: + if os.path.splitext(file)[1] == ".jpg": + filelist.append(os.path.join(root, file)) + return filelist +def im_xiangsu(paths): + for filename in paths: + try: + im = Image.open(filename) + newim = im.resize((128, 128)) + + newim.save( filename[:-4]+ '-convert.jpg') + # delete the old file + os.remove(filename) + print('图片' + filename[:-4] + '像素转化完成') + except OSError as e: + print(e.args) + + +if __name__ == '__main__': + test_path = './' + filelist=read_image(test_path) + print(filelist) + im_xiangsu(filelist) +``` + +### 3、split_test_train.py + +这个代码要运行流程要修改`self.path = "./haze"`和`demo1.selectFile(path6,0.05)`以及`demo2.selectFile(test_path6,0.01)`。 + +其中,haze对应6,thunder对应5,snow对应4,cloud对应3,sunny对应2,rainy对应1。 + +```python +## 由于训练的数据太多,所以需要将数据分成训练集和测试集 +import os +import shutil +import random + +# train_path +path1 = r'./lin_train/_rainy' +path2 = r'./lin_train/_sunny' +path3 = r'./lin_train/_cloudy' +path4 = r'./lin_train/_snow' +path5 = r'./lin_train/_thunder' +path6 = r'./lin_train/_haze' + +# test_path +test_path1 = './lin_test/_rainy_' +test_path2 = './lin_test/_sunny_' +test_path3 = './lin_test/_cloudy_' +test_path4 = './lin_test/_snow_' +test_path5 = './lin_test/_thunder_' +test_path6 = './lin_test/_haze_' + + +class BatchRename(): + ''' + 批量重命名文件夹中的图片文件 + ''' + def __init__(self): + # 此处应该为自己存放图片文件夹的路径 + # self.path = "./rainy" + # self.path = r"./sunny" + # self.path = r"./cloudy" + # self.path = "./snow" + # self.path = r"./thunder" + self.path = "./haze" + + def selectFile(self, path, rate): + # 从path中随机选取rate比例的文件 + pathDir = os.listdir(self.path) + filenumber = len(pathDir) + picknumber = int(filenumber * rate) + # + sample = random.sample(pathDir, picknumber) + print(sample) + # # clear all the file in "./lin_test/_thunder_" + for file in os.listdir(path): + os.remove(os.path.join(path, file)) + for name in sample: + origin = os.path.join(self.path, name) + #print(origin) + # copy file "./thunder" to "./lin_test/_thunder_" and "./lin_train/_thunder" + target = os.path.join(path, name) + shutil.copy2(origin, target) + print("move done!") + return + + +# sunny +demo1 = BatchRename() +demo1.selectFile(path6,0.05) +demo2 = BatchRename() +demo2.selectFile(test_path6,0.01) +``` + +### 4、img_label_and_model.py + +```python +import os +from PIL import Image +import numpy as np +import keras +from keras import datasets,layers,models + + +def im_array(paths): + ''' + 将图片转化为数组 + :param paths: 读入图片的路径 + :return: + M: 图片的数组,shape为(图片数量,图片的长,图片的宽,图片的通道数) + ''' + M = [] + for filename in paths: + im = Image.open(filename) + im_L = im.convert("L") # 模式L + Core = im_L.getdata() # 获取图像像素值 + arr1 = np.array(Core, dtype='float32') / 255.0 # 将像素值转化为0-1之间的数 + list_img = arr1.tolist() # 将数组转化为列表,方便后面的reshape, + M.extend(list_img) # 将列表中的元素添加到M中 + return M + + +def read_image(paths): + os.listdir(paths) + filelist = [] + for root, dirs, files in os.walk(paths): + for file in files: + if os.path.splitext(file)[1] == ".jpg": + filelist.append(os.path.join(root, file)) + return filelist + + +path1 = './lin_train/_rainy' +path2 = './lin_train/_sunny' +path3 = './lin_train/_cloudy' +path4 = './lin_train/_snow' +path5 = './lin_train/_thunder' +path6 = './lin_train/_haze' +filelist_1 = read_image(path1) +filelist_2 = read_image(path2) +filelist_3 = read_image(path3) +filelist_4 = read_image(path4) +filelist_5 = read_image(path5) +filelist_6 = read_image(path6) +filelist_all = filelist_1 + filelist_2+filelist_3+filelist_4+filelist_5+filelist_6 + +M = [] +M = im_array(filelist_all) # 将图片转化为数组 + +dict_label={0:'雨天',1:'晴天',2:'多云',3:'雪天',4:'雷天',5:'雾天'} +train_images=np.array(M).reshape(len(filelist_all),128,128) +label=[0]*len(filelist_1)+[1]*len(filelist_2)+[2]*len(filelist_3)+[3]*len(filelist_4)+[4]*len(filelist_5)+[5]*len(filelist_6) +train_lables=np.array(label) #数据标签 +train_images = train_images[ ..., np.newaxis ] #数据图片 + +test_path1 = './lin_test/_rainy_' +test_path2 = './lin_test/_sunny_' +test_path3 = './lin_test/_cloudy_' +test_path4 = './lin_test/_snow_' +test_path5 = './lin_test/_thunder_' +test_path6 = './lin_test/_haze_' + +test_filelist_1 = read_image(test_path1) +test_filelist_2 = read_image(test_path2) +test_filelist_3 = read_image(test_path3) +test_filelist_4 = read_image(test_path4) +test_filelist_5 = read_image(test_path5) +test_filelist_6 = read_image(test_path6) +filelist_tot = test_filelist_1+ test_filelist_2+test_filelist_3+test_filelist_4+test_filelist_5+test_filelist_6 + +N = [] +N = im_array(filelist_tot) + +dict_label={0:'雨天',1:'晴天',2:'多云',3:'雪天',4:'雷天',5:'雾天'} +test_images=np.array(N).reshape(len(filelist_tot),128,128) +label=[0]*len(test_filelist_1)+[1]*len(test_filelist_2)+[2]*len(test_filelist_3)+[3]*len(test_filelist_4)+[4]*len(test_filelist_5)+[5]*len(test_filelist_6) +test_lables=np.array(label) #数据标签 +test_images = test_images[ ..., np.newaxis ] #数据图片,...表示前面的维度不变,np.newaxis表示增加一个维度 + +# 进入神经网络 +model = models.Sequential() +# Conv2D表示卷积层,activation表示激活函数 +model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(128, 128, 1))) +# MaxPooling2D表示最大池化层 +model.add(layers.MaxPooling2D((2, 2))) + +model.add(layers.Conv2D(64, (3, 3), activation='relu')) +model.add(layers.MaxPooling2D((2, 2))) +model.add(layers.Conv2D(64, (3, 3), activation='relu')) + +# Flatten表示展平层,将多维张量展平为一维向量 +model.add(layers.Flatten()) + +# Dense表示全连接层, +# 这两行代码分别添加了两个全连接层,第一个全连接层包含64个神经元,激活函数为ReLU;第二个全连接层包含2个神经元,激活函数为softmax,用于输出模型的分类结果。 +model.add(layers.Dense(64, activation='relu')) +model.add(layers.Dense(6, activation='softmax')) +model.summary() # 显示模型的架构 +model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['accuracy']) +#epochs为训练多少轮、batch_size为每次训练多少个样本 +model.fit(train_images, train_lables, epochs=1000) + +print(model.evaluate(test_images,test_lables)) # 这里的test_images和test_lables是测试集的数据和标签 + +# save model to h5 +model.save('model_epochs=1000.h5') +``` + +### 5、predict.py + +```python +import os +from PIL import Image +import numpy as np +# load model from h5 +from tensorflow import keras +model = keras.models.load_model('./model_epochs=1000.h5') +#model.summary() + + +def read_image(paths): + os.listdir(paths) + filelist = [] + for root, dirs, files in os.walk(paths): + for file in files: + if os.path.splitext(file)[1] == ".jpg": + filelist.append(os.path.join(root, file)) + return filelist + + +def im_xiangsu(paths): + for filename in paths: + try: + im = Image.open(filename) + newim = im.resize((128, 128)) + + newim.save( filename[:-4]+ '-convert.jpg') + # delete the old file + os.remove(filename) + print('图片' + filename[:-4] + '像素转化完成') + except OSError as e: + print(e.args) +def im_s_array(paths,i): + im = Image.open(paths[i]) # 读取图片 [0]是为了读取第一张图片 + im_L = im.convert("L") # 模式L + Core = im_L.getdata() + arr1 = np.array(Core, dtype='float32') / 255.0 + list_img = arr1.tolist() + images = np.array(list_img).reshape(-1, 128, 128, 1) + return images + +# load image +test = './lin_predict' # 你要测试的图片的路径 +filelist = read_image(test) +im_xiangsu(filelist) +filelist = read_image(test) + + +# 依次读入filelist中的图片 +for i in range(len(filelist)): + + img = im_s_array(filelist,i) + + # 预测图像 + dict_label={0:'雨天',1:'晴天',2:'多云',3:'雪天',4:'雷天',5:'雾天'} + predictions_single = model.predict(img) + print(filelist[i],"的预测结果为:", dict_label[np.argmax(predictions_single)]) + # 这里返回数组中概率最大的那个 + print(predictions_single) +``` + +### 6、结果展示 + +这个瓜皮模型,雷天和雨天分不清,有时候晴天和多云和雨天也分不清。 diff --git "a/_posts/2023-04-15-Django\345\210\235\346\216\242.md" "b/_posts/2023-04-15-Django\345\210\235\346\216\242.md" new file mode 100644 index 0000000000..448b5d0c07 --- /dev/null +++ "b/_posts/2023-04-15-Django\345\210\235\346\216\242.md" @@ -0,0 +1,130 @@ +--- +layput: post +title: Django初探 +date: 2023-04-15 +description: "Django初探" +tag: Python +--- + +# 安装 + +```bash +pip install django +``` + +查看 + +```bash +django-admin --version +``` + +查看命令 + +```bash +django-admin +``` + + +## 启动项目 + +以下命令用于创建一个叫做guest的项目 + +```bash +django-admin startproject guest +``` + +![20230415083853](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230415083853.png) + + +guest/__init__.py:一个空的文件,用它标识一个目录为Python 标准的包。 + +guest/asgi.py:为ASGI 兼容的Web 服务器服务项目的切入点。 + +guest/settings.py:Django 项目的配置文件,包括Django 模块应用配置,数据库配置,模板配置等。 + +guest/urls.py:Django 项目的URL 声明。 +guest/wsgi.py:为WSGI 兼容的Web 服务器服务项目的切入点。 + +manage.py:一个命令行工具,可以让你用Django 项目以不同的方式进行交互。 + +## 启动服务 + +```bash +cd guest +python manage.py runserver +``` + +![20230415084450](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230415084450.png) + +默认的端口号是8000,也可以在启动时候指定IP地址和端口号。 + +```bash +python manage.py runserver 192.168.1.5:8001 +``` + + +manage.py 提供的许多命令与django-admin 相同。 + +可以使用startapp 命令创建一个叫做sign的应用 + +```bash +django-admin startapp sign +``` + +会发现新生成了一个sign文件夹: + +![20230415084659](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230415084659.png) + +migrations/:用于记录models 中数数据的变更。 +admin.py:映射models 中的数据到Django 带自的admin 后台。 +apps.py:在新的Django 版本中新增,用于应用程序的配置。 +models.py:创建应用程序数据表模型(对应数据库的相关操作)。 +tests.py:创建Django 测试。 +views.py:控制向前端显示哪些数据。 + +### Hello,Django项目 + + + +首先需要配置一下guest/settings.py 文件,将sign 应用添加到项目中。 + +```python +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'sign', # 添加应用 +] +``` + + +在浏览器中输入地址后,Django会先去guest项目子目录下的urls.py文件中去寻找访问的路径,所以首先去添加/index/的路由配置。 + +接下来,我们要考虑在哪个路径下显示Hello,Django。在guest/urls.py 文件中添加如下代码: + +```python +from django.contrib import admin +from django.urls import path +from guest.sign import views # 导入sign应用views文件 + +urlpatterns = [ + path('admin/', admin.site.urls), + path('index/', views.index), # 添加index 路径配置 +] +``` + +这次项目启动不起来,因为我们在views.py 文件中并没有index 属性,所以我们需要在sign 应用下创建views.py 文件。(路径加上了,找到路径后要做什么呢,就要去应用中找配置views文件里的index,看他让我们的浏览器做什么) + +```python +from django.http import HttpResponse + +# Create your views here. +def index(request): + return HttpResponse("Hello Django!") +``` + + +定义index 函数,并通过HttpResponse 类向页面返回字符串“Hello Django!”。 \ No newline at end of file diff --git "a/_posts/2023-04-17-\350\257\273\345\217\226\346\226\207\344\273\266\345\244\271\344\270\213\346\211\200\346\234\211\346\226\207\344\273\266\345\271\266\346\217\220\345\217\226\347\211\271\345\256\232\344\277\241\346\201\257.md" "b/_posts/2023-04-17-\350\257\273\345\217\226\346\226\207\344\273\266\345\244\271\344\270\213\346\211\200\346\234\211\346\226\207\344\273\266\345\271\266\346\217\220\345\217\226\347\211\271\345\256\232\344\277\241\346\201\257.md" new file mode 100644 index 0000000000..789da73f81 --- /dev/null +++ "b/_posts/2023-04-17-\350\257\273\345\217\226\346\226\207\344\273\266\345\244\271\344\270\213\346\211\200\346\234\211\346\226\207\344\273\266\345\271\266\346\217\220\345\217\226\347\211\271\345\256\232\344\277\241\346\201\257.md" @@ -0,0 +1,122 @@ +--- +layout: post +title: 读取文件夹下所有文件并提取特定信息 +date: 2023-04-17 +description: "读取文件夹下所有文件并提取特定信息" +tag: Python +--- + +## 场景描述 + +前期已经使用SHELL脚本备份了每日的syslog日志,现在需要将这些日志文件中的特定信息提取出来,然后存储到数据库中。 + +比如说包含`error`的日志信息,需要提取出来;包含`warning`的日志信息,需要提取出来;包含`info`的日志信息,需要提取出来。 + + +## 读取文件夹下所有文件 + +创建一个file_analysis.py文件,定义一个readfile类,包含两个方法。 + +def get_log_files(path):获取当前目录下所有以`syslog_`开头的文件,并返回一个列表。 + +def read_file(file):逐行读取文件,并把文件内容保存到一个列表中。 + +```python +import os + +class readfile(): + + def get_log_files(path): + """Get a list of log files from the current directory.""" + log_files = [] + for file in os.listdir(path): + if file.startswith('syslog_'): + log_files.append(file) + return log_files + + # read lines from the file and then save it to a list + def read_file(file): + """Read a file and return a list of lines.""" + with open(file, 'r',encoding='utf-8') as f: + lines = f.readlines() + return lines + + + +if __name__ == '__main__': + + # input() + file_content = readfile.read_file('./log_01/syslog_230328') + readfile.get_error_lines_and_print(file_content) + +``` + +## 提取特定信息 + +创建一个information.py文件,包含两个方法。 + +def get_error_lines_and_print(lines):获取包含`error`的日志信息,并打印出来。 +def get_warning_lines_and_print(lines):获取包含`warning`的日志信息,并打印出来。 + +```python +import os + +# get the error lines +def get_error_lines_and_print(lines): + """Get a list of error lines.""" + error_lines = [] + for line in lines: + if 'error' in line or 'Error' in line or 'ERROR' in line: + error_lines.append(line) + outputfilename='error_'+lines[1][:3]+'_'+lines[1][4:6]+'.txt' + # if erro_line is empty, print 'No error found' + if len(error_lines) == 0: + with open(outputfilename, 'w') as f: + f.write('No error found') + # if error_line is not empty, print the error lines and save it to a file + else: + with open(outputfilename, 'w') as f: + for error_line in error_lines: + f.write(error_line) + + +# get the warning lines +def get_warning_lines_and_print(lines): + """Get a list of warning lines.""" + warning_lines = [] + for line in lines: + if 'warning' in line or 'Warning' in line or 'WARNING' in line: + warning_lines.append(line) + outputfilename='warning_'+lines[1][:3]+'_'+lines[1][4:6]+'.txt' + # if warning_line is empty, print 'No warning found' + if len(warning_lines) == 0: + with open(outputfilename, 'w') as f: + f.write('No warning found') + # if warning_line is not empty, print the warning lines and save it to a file + else: + with open(outputfilename, 'w') as f: + for warning_line in warning_lines: + f.write(warning_line) +``` + +## 调用,大循环 + + +这个是主函数,调用上面两个文件中的方法,实现读取文件夹下所有文件并提取特定信息。 + +```python +from file_analysis import * +from information import get_error_lines_and_print, get_warning_lines_and_print + + +def loop(): + file_list = readfile.get_log_files('./log_03/') + for file in file_list: + file_content = readfile.read_file('./log_03/'+file) + get_error_lines_and_print(file_content) + get_warning_lines_and_print(file_content) + + +if __name__ == '__main__': + loop() +``` \ No newline at end of file diff --git "a/_posts/2023-04-17-\350\267\237\347\235\200ChatGPTplus\346\217\222\344\273\266\345\255\246\344\271\240\346\216\222\345\272\217\347\256\227\346\263\225.md" "b/_posts/2023-04-17-\350\267\237\347\235\200ChatGPTplus\346\217\222\344\273\266\345\255\246\344\271\240\346\216\222\345\272\217\347\256\227\346\263\225.md" new file mode 100644 index 0000000000..f4d8193061 --- /dev/null +++ "b/_posts/2023-04-17-\350\267\237\347\235\200ChatGPTplus\346\217\222\344\273\266\345\255\246\344\271\240\346\216\222\345\272\217\347\256\227\346\263\225.md" @@ -0,0 +1,262 @@ +--- +layout: post +title: 跟着ChatGPTplus插件学习排序算法 +date: 2023-04-17 +description: "跟着ChatGPTplus插件学习排序算法" +tag: Python +--- +## 插件下载地址 + +pycharm-文件-设置-插件-搜索ChatGPTplus: + +![20230417191441](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230417191441.png) + +## prompt举例 + +插入排序算法,需要有及其详细的注释和print,并给出例子 + +## 冒泡排序算法 + +```python +def bubble_sort(arr): + """ + 冒泡排序算法 + :param arr: 待排序的列表 + :return: 排序后的列表 + """ + n = len(arr) + # 遍历n-1次,每次将最大值移动到末尾 + for i in range(n-1): + # 每次遍历比较相邻的两个元素 + for j in range(n-i-1): + # 如果前一个元素大于后一个元素,则交换它们的位置 + if arr[j] > arr[j+1]: + arr[j], arr[j+1] = arr[j+1], arr[j] + print(f"第{i+1}次遍历后的结果:{arr}") + return arr + +if __name__ == '__main__': + arr = [3, 5, 4, 1, 2, 6] + print(f"排序前的列表:{arr}") + print(f"排序后的列表:{bubble_sort(arr)}") +``` + +结果如下: + +```console +排序前的列表:[3, 5, 4, 1, 2, 6] +第1次遍历后的结果:[3, 4, 1, 2, 5, 6] +第2次遍历后的结果:[3, 1, 2, 4, 5, 6] +第3次遍历后的结果:[1, 2, 3, 4, 5, 6] +第4次遍历后的结果:[1, 2, 3, 4, 5, 6] +第5次遍历后的结果:[1, 2, 3, 4, 5, 6] +排序后的列表:[1, 2, 3, 4, 5, 6] +``` + +## 二分法搜索算法 + +乱入了一个奇奇怪怪的东西 + +```python +def binary_search(arr, target): + """ + 二分法搜索算法 + + :param arr: 有序数组 + :param target: 目标值 + :return: 目标值的索引或者 None + """ + left = 0 # 左边界 + right = len(arr) - 1 # 右边界 + + while left <= right: + mid = (left + right) // 2 # 取中间值 + + if arr[mid] == target: # 如果中间值等于目标值,返回索引 + return mid + elif arr[mid] < target: # 如果中间值小于目标值,在右半部分寻找 + left = mid + 1 + else: # 如果中间值大于目标值,在左半部分寻找 + right = mid - 1 + + return None # 没有找到目标值,返回 None + +if __name__ == '__main__': + arr = [1, 2, 3, 4, 5, 6] + print(f"二分法搜索算法:{binary_search(arr, 5)}") + print(f"二分法搜索算法:{binary_search(arr, 7)}") + +''' +假设我们要在一个有序数组中查找一个特定的元素,可以使用二分法算法。 +二分法算法是一种高效的算法,通常用于在大型、有序数据集中查找特定的值。 + +算法的基本思想是将数组分成两部分,然后比较中间值和目标值。 +如果中间值等于目标值,则返回中间值的索引。 +如果中间值大于目标值,则在左半部分中查找。 +如果中间值小于目标值,则在右半部分中查找。反复进行这个过程,直到找到目标值或者确定目标值不存在。 +''' +``` + +结果如下: + +```console +二分法搜索算法:4 +二分法搜索算法:None +``` + +## 选择排序算法 + +选择排序算法是一种简单直观的排序算法,它的基本思想是每次找到未排序部分中的最小元素,然后将其放到已排序部分的末尾。 + +接下来,我们用一个例子来演示选择排序算法的执行过程。 + +假设待排序数组为[64, 25, 12, 22, 11],则选择排序算法的执行过程如下: + +首先,未排序部分包括整个数组,已排序部分为空。 + +第一次循环,找到未排序部分的最小元素11,将其与已排序部分的末尾交换位置,得到[11, 25, 12, 22, 64]。此时已排序部分包括元素11。 + +第二次循环,找到未排序部分的最小元素12,将其与已排序部分的末尾交换位置,得到[11, 12, 25, 22, 64]。此时已排序部分包括元素11和12。 + +第三次循环,找到未排序部分的最小元素22,将其与已排序部分的末尾交换位置,得到[11, 12, 22, 25, 64]。此时已排序部分包括元素11、12和22。 + +第四次循环,找到未排序部分的最小元素25,将其与已排序部分的末尾交换位置,得到[11, 12, 22, 25, 64]。此时已排序部分包括元素11、12、22和25。 + +最后,未排序部分为空,排序完成。 + +```python +def selection_sort(arr): + n = len(arr) + for i in range(n): + # 找到未排序部分的最小元素 + min_idx = i + for j in range(i+1, n): + if arr[j] < arr[min_idx]: + min_idx = j + # 将最小元素放到已排序部分的末尾 + arr[i], arr[min_idx] = arr[min_idx], arr[i] + # 打印每次交换后的数组 + print(arr) + +# 示例 +arr = [64, 25, 12, 22, 11] +selection_sort(arr) +print("排序后的数组:", arr) + +``` + +输出结果如下: + +```console +[11, 25, 12, 22, 64] +[11, 12, 25, 22, 64] +[11, 12, 22, 25, 64] +[11, 12, 22, 25, 64] +[11, 12, 22, 25, 64] +排序后的数组: [11, 12, 22, 25, 64] +``` + +## 插入排序 + +插入排序算法的基本思想是将未排序的元素插入到已排序的元素中,从而得到一个有序的序列。具体实现方式是,先将第一个元素视为已排序的序列,然后逐个将未排序的元素插入到已排序的序列中,直到所有元素都被插入。 + +```python +def insertion_sort(arr): + # 遍历整个数组 + for i in range(1, len(arr)): + # 将待排序元素存储在临时变量中 + temp = arr[i] + j = i - 1 + # 将已排序元素中比待排序元素大的元素向右移动 + while j >= 0 and arr[j] > temp: + arr[j + 1] = arr[j] + j -= 1 + # 将待排序元素插入到正确位置 + arr[j + 1] = temp + + # 打印每次排序后的数组 + print(arr) + return arr + +# 示例 +arr = [3,1,4,5,2] +print(f"排序前的列表:{arr}") +print(f"排序后的列表:{insertion_sort(arr)}") +``` + +输出结果如下: + +```console +排序前的列表:[3, 1, 4, 5, 2] +[1, 3, 4, 5, 2] +[1, 3, 4, 5, 2] +[1, 3, 4, 5, 2] +[1, 2, 3, 4, 5] +排序后的列表:[1, 2, 3, 4, 5] +``` + +## 希尔排序 + +希尔排序是一种插入排序的改进算法,也称为缩小增量排序。它通过将待排序的数组分割成几个子数组来提高插入排序的性能,每个子数组使用插入排序进行排序,最后再合并为完整的有序数组。 + +```python +def shell_sort(arr): + # 计算数组 arr 的长度 n + n = len(arr) + # 计算初始的增量 gap,通常设置为 n 的一半 + gap = n // 2 + # 进入一个 while 循环,当 gap 大于 0 时,执行以下操作 + while gap > 0: + # 从 gap 开始,对数组 arr 进行插入排序, + #具体实现是从 i = gap 开始,依次将 arr[i] 插入到 arr[0] 到 arr[i-gap] 的位置上,这里的 arr[0] 到 arr[i-gap] 就是一个子序列 + for i in range(gap, n): + temp = arr[i] + j = i + while j >= gap and arr[j - gap] > temp: + arr[j] = arr[j - gap] + j -= gap + arr[j] = temp + # 每次插入排序结束后,将 gap 除以 2,继续进行下一轮排序 + gap //= 2 + return arr + +if __name__ == 'main': + arr = [64, 34, 25, 12, 22, 11, 90] + print("Original array:", arr) + sorted_arr = shell_sort(arr) + print("Sorted array:", sorted_arr) +``` + +在这个实现中,我们使用了一个变量 `gap`来表示子序列的间隔大小。开始时,`gap`的大小为序列长度的一半。然后,在每次循环中,我们将 `gap`的大小减半,直到 `gap`等于1。 + +在每次循环中,我们对每个子序列进行插入排序。具体来说,对于第 `i`个子序列,我们将它的第 `gap`个元素作为起点,然后将它后面每个 `gap`个元素与前面已经排好序的子序列进行比较,插入到正确的位置。 + +在这个例子中,我们将待排序的数组分割成2个子数组,分别为[64, 25, 22, 90]和[34, 12, 11]。然后对每个子数组进行插入排序,最后将它们合并为一个完整的有序数组。 + +输出结果如下: + +```console +Original array: [64, 34, 25, 12, 22, 11, 90] +Sorted array: [11, 12, 22, 25, 34, 64, 90] +``` + +# 下期预告 + +## 归并排序 + +## 堆排序 + +## 计数排序 + +## 桶排序 + +## 基数排序 + +## 对比 + +![20230417191727](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230417191727.png) + + +## 参考链接 + +[排序算法汇总](https://www.cnblogs.com/herozhi/p/5880939.html) \ No newline at end of file diff --git "a/_posts/2023-04-18-\350\267\237\347\235\200ChatGPTplus\346\217\222\344\273\266\345\255\246\344\271\240\347\256\227\346\263\225\357\274\210\344\270\213\357\274\211.md" "b/_posts/2023-04-18-\350\267\237\347\235\200ChatGPTplus\346\217\222\344\273\266\345\255\246\344\271\240\347\256\227\346\263\225\357\274\210\344\270\213\357\274\211.md" new file mode 100644 index 0000000000..2188988945 --- /dev/null +++ "b/_posts/2023-04-18-\350\267\237\347\235\200ChatGPTplus\346\217\222\344\273\266\345\255\246\344\271\240\347\256\227\346\263\225\357\274\210\344\270\213\357\274\211.md" @@ -0,0 +1,275 @@ +--- +layout: post +title: 跟着ChatGPTplus插件学习算法(下) +date: 2023-04-18 +description: "跟着ChatGPTplus插件学习算法(下)" +tag: Python +--- +## 归并排序 + +### 写一个归并排序算法,要求有良好的注释和print每一次排序的过程结果,并举例子 + +```python +def merge_sort(arr): + # 递归终止条件,当数组长度为1时,直接返回 + if len(arr) == 1: + return arr + + # 将数组分为两半 + mid = len(arr) // 2 + left = arr[:mid] + right = arr[mid:] + + # 对左右两半分别进行归并排序 + left_sorted = merge_sort(left) + right_sorted = merge_sort(right) + + # 合并两个有序数组 + result = [] + i = j = 0 + while i < len(left_sorted) and j < len(right_sorted): + if left_sorted[i] < right_sorted[j]: + result.append(left_sorted[i]) + i += 1 + else: + result.append(right_sorted[j]) + j += 1 + + if i == len(left_sorted): + result += right_sorted[j:] + else: + result += left_sorted[i:] + + # 打印每次排序结果 + print("排序结果:", result) + + return result + + +# 测试 +arr = [12, 45, 23, 67, 89, 34, 56, 78] +print("原始数组:", arr) +sorted_arr = merge_sort(arr) +print("最终排序结果:", sorted_arr) +``` + +结果如下: + +```console +原始数组: [12, 45, 23, 67, 89, 34, 56, 78] +排序结果: [12, 45] +排序结果: [23, 67] +排序结果: [12, 23, 45, 67] +排序结果: [34, 89] +排序结果: [56, 78] +排序结果: [34, 56, 78, 89] +排序结果: [12, 23, 34, 45, 56, 67, 78, 89] +最终排序结果: [12, 23, 34, 45, 56, 67, 78, 89] +``` + +## 堆排序 + +### 写一个堆排序算法,要求有良好的注释和print每一次排序的过程结果,并举例子 + +```python +# 堆排序算法 +def heap_sort(arr): + n = len(arr) + + # 构建最大堆 + for i in range(n // 2 - 1, -1, -1): + heapify(arr, n, i) + + # 一个个取出元素 + for i in range(n - 1, 0, -1): + arr[i], arr[0] = arr[0], arr[i] # 交换 + heapify(arr, i, 0) + print(arr) # 打印每一次排序的过程结果 + +# 堆化子树 +def heapify(arr, n, i): + largest = i # 初始化最大元素为根节点 + l = 2 * i + 1 # 左子节点 + r = 2 * i + 2 # 右子节点 + + # 如果左子节点比根节点大,则更新最大元素 + if l < n and arr[l] > arr[largest]: + largest = l + + # 如果右子节点比根节点大,则更新最大元素 + if r < n and arr[r] > arr[largest]: + largest = r + + # 如果最大元素不是根节点,则交换 + if largest != i: + arr[i], arr[largest] = arr[largest], arr[i] + heapify(arr, n, largest) + +# 测试 +arr = [12, 11, 13, 5, 6, 7] +heap_sort(arr) +print("排序后的数组:") +print(arr) +``` + +结果如下: + +```console +[12, 11, 7, 5, 6, 13] +[11, 6, 7, 5, 12, 13] +[7, 6, 5, 11, 12, 13] +[6, 5, 7, 11, 12, 13] +[5, 6, 7, 11, 12, 13] +排序后的数组: +[5, 6, 7, 11, 12, 13] +``` + +堆是一种特殊的树形数据结构,它满足以下两个性质: + +1. 堆是一个完全二叉树,即除了最后一层,其他层的节点都是满的,最后一层的节点都靠左排列。 +2. 堆中每个节点的值都大于等于(或小于等于)其子节点的值,这被称为堆的性质。 + +堆可以分为最大堆和最小堆。在最大堆中,每个节点的值都大于等于其子节点的值,而在最小堆中,每个节点的值都小于等于其子节点的值。 + +堆排序算法利用最大堆的性质来进行排序。具体来说,它首先构建一个最大堆,然后将堆顶元素(即最大元素)与最后一个元素交换,然后将剩余元素重新构建为最大堆,重复此过程直到所有元素都有序排列。堆排序的时间复杂度为O(nlogn)。 + +回到您的问题,您之前问的是堆排序算法中用来构建最大堆的for循环。具体来说,它遍历数组中对应于非叶子节点的索引(即至少有一个子节点的节点)。循环从最后一个非叶子节点的索引开始(即n // 2 - 1,其中n是数组的长度),向根节点(索引为0)反向迭代。 + +在循环的每次迭代中,heapify函数被调用以确保以该节点为根的子树满足最大堆的性质。这是通过将节点与其子节点进行比较并在必要时与较大的子节点交换来完成的。循环有效地将数组中较大的元素“冒泡”到它们在堆中的正确位置。 + + + + +## 计数排序 + +### 写一个计数排序算法,要求有良好的注释和print每一次排序的过程结果,并举例子 + +```python +def counting_sort(arr): + # 找到数组中的最大值 + max_val = max(arr) + # 初始化计数数组 + count_arr = [0] * (max_val + 1) + # 计算每个元素出现的次数 + for i in arr: + count_arr[i] += 1 + # 对计数数组进行累加 + for i in range(1, len(count_arr)): + count_arr[i] += count_arr[i-1] + # 初始化排序后的数组 + sorted_arr = [0] * len(arr) + # 遍历原始数组,将元素放入排序后的数组中 + for i in arr: + sorted_arr[count_arr[i]-1] = i + count_arr[i] -= 1 + # 打印每一次排序的结果 + print(sorted_arr) + return sorted_arr + +# 举例 +arr = [3, 1, 6, 2, 4, 3] +counting_sort(arr) +``` + +结果如下: + +```console +[1, 2, 3, 3, 4, 6] +``` + +## 桶排序 + +### 写一个桶排序算法,要求有良好的注释和print每一次排序的过程结果,并举例子 + +```python +# 定义桶排序函数 +def bucket_sort(arr): + # 获取数组中的最大值和最小值 + max_num = max(arr) + min_num = min(arr) + # 计算桶的数量 + bucket_num = (max_num - min_num) // len(arr) + 1 + # 初始化桶 + buckets = [[] for _ in range(bucket_num)] + # 将元素放入桶中 + for num in arr: + index = (num - min_num) // len(arr) + buckets[index].append(num) + # 对每个桶进行排序 + for bucket in buckets: + bucket.sort() + print(bucket) + # 将桶中的元素按顺序放回原数组 + res = [] + for bucket in buckets: + res += bucket + return res + +# 测试 +arr = [3, 1, 4, 2, 5] +print(bucket_sort(arr)) # [1, 2, 3, 4, 5] +``` + +结果如下: + +```console +[1, 2] +[3, 4] +[5] +[1, 2, 3, 4, 5] +``` + +## 基数排序 + +### 写一个基数排序算法,要求有良好的注释和print每一次排序的过程结果,并举例子 + +```python +def radix_sort(arr): + """ + 基数排序算法 + :param arr: 待排序数组 + :return: 排序后的数组 + """ + # 获取数组中最大值的位数 + max_num = max(arr) + max_digit = len(str(max_num)) + + # 从个位开始,对数组按照该位进行排序 + for i in range(max_digit): + # 初始化桶 + buckets = [[] for _ in range(10)] + + # 将数组中的元素放入桶中 + for num in arr: + # 获取当前位数的数字 + digit = (num // (10 ** i)) % 10 + buckets[digit].append(num) + + # 将桶中的元素按照顺序放回数组中 + arr = [] + for bucket in buckets: + arr.extend(bucket) + + # 打印每一次排序的结果 + print(f"Sorting at digit {i+1}: {arr}") + + return arr + +# 举例 +arr = [170, 45, 75, 90, 802, 24, 2, 66] +sorted_arr = radix_sort(arr) +print(sorted_arr) +``` + +结果如下: + +```console +Sorting at digit 1: [170, 90, 802, 2, 24, 45, 75, 66] +Sorting at digit 2: [802, 2, 24, 45, 66, 170, 75, 90] +Sorting at digit 3: [2, 24, 45, 66, 75, 90, 170, 802] +[2, 24, 45, 66, 75, 90, 170, 802] +``` + +## 参考链接 + +[排序算法:堆排序【图解+代码】_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV1fp4y1D7cj/?spm_id_from=333.337.search-card.all.click&vd_source=a59f9331c2b80795dda1842d7be401ec) diff --git "a/_posts/2023-04-19-\344\277\256\346\224\271Windows\344\270\255 pip \347\232\204\347\274\223\345\255\230\344\275\215\347\275\256\344\270\216\345\210\240\351\231\244 pip \347\274\223\345\255\230.md" "b/_posts/2023-04-19-\344\277\256\346\224\271Windows\344\270\255 pip \347\232\204\347\274\223\345\255\230\344\275\215\347\275\256\344\270\216\345\210\240\351\231\244 pip \347\274\223\345\255\230.md" new file mode 100644 index 0000000000..b16f6abbef --- /dev/null +++ "b/_posts/2023-04-19-\344\277\256\346\224\271Windows\344\270\255 pip \347\232\204\347\274\223\345\255\230\344\275\215\347\275\256\344\270\216\345\210\240\351\231\244 pip \347\274\223\345\255\230.md" @@ -0,0 +1,53 @@ +--- +layout: post +title: "修改Windows中 pip 的缓存位置与删除 pip 缓存" +date: 2023-04-19 +desc: "修改Windows中 pip 的缓存位置与删除 pip 缓存" +tags: Python +--- +# 修改Windows中 pip 的缓存位置与删除 pip 缓存 + +下载安装Python 库时,是安装版的话一般都通过 pip install xxx 来安装包。但安装下载的文件都会缓存下来,而且默认都在C盘。最近C盘要G了,导致好多包安装失败。 + +哪怕是下载失败,缓存也会默认放在C盘中 + +### 所以改变pip缓存位置 + +首先先建立一个文件夹来充当缓存目录,比如说就在D盘下面的pipCache目录。 + +WIN+R命令行窗口 + +```shell +pip config set global.cache-dir "D:\pipCache" +1 +``` + +> 注:文件夹路径地址要如我这样的绝对地址。 + +![20230419095403](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230419095403.png) + +如果出现第二行的语句,说明更改成功。 + +然后就可以删除之前的pip缓存了,也可以把其复制来这个新地址。 + + + +### 删除 pip 缓存 + +在文件资源管理器(这个指的是最上面文件路径)输入下面语句回车。 + +```shell +%LocalAppData% +``` + + +找到pip文件夹删除即可 + +### 配置pip.ini文件 + +在文件资源器软件的路径框输入 `%APPDATA%` 回车 + +在 `Roaming`文件夹下找到 `pip`文件夹,**如果没有就新建一个** `pip.ini`文件 + +> 如果后面测试有问题,可能是系统自带笔记本编码问题。就改用你写代码的编辑器新建 `pip.ini`文件,要utf-8的编码。 +> diff --git "a/_posts/2023-04-20-FlaskWeb\345\274\200\345\217\221\345\237\272\344\272\216Python\347\232\204Web\345\272\224\347\224\250\345\274\200\345\217\221\345\256\236\346\210\230-\350\257\273\344\271\246\347\254\224\350\256\260.md" "b/_posts/2023-04-20-FlaskWeb\345\274\200\345\217\221\345\237\272\344\272\216Python\347\232\204Web\345\272\224\347\224\250\345\274\200\345\217\221\345\256\236\346\210\230-\350\257\273\344\271\246\347\254\224\350\256\260.md" new file mode 100644 index 0000000000..ca2936f217 --- /dev/null +++ "b/_posts/2023-04-20-FlaskWeb\345\274\200\345\217\221\345\237\272\344\272\216Python\347\232\204Web\345\272\224\347\224\250\345\274\200\345\217\221\345\256\236\346\210\230-\350\257\273\344\271\246\347\254\224\350\256\260.md" @@ -0,0 +1,149 @@ +--- +layout: post +title: "FlaskWeb开发基于Python的Web应用开发实战-读书笔记" +date: 2023-04-20 +description: "FlaskWeb开发基于Python的Web应用开发实战读书笔记" +tag: Python +--- +## 什么是Flask + +Flask是一个基于Python的轻量级Web应用框架,它有两个主要依赖:路由、调试和Web服务器网关接口(Web Server Gateway Interface,WSGI)子系统由Werkzeug提供,模板引擎则由Jinja2提供。 + +Flask并不原生支持数据库,但是它提供了一个扩展,可以使用Flask-SQLAlchemy来访问数据库。 + +## Flask的安装 + +虚拟环境使用第三方实现工具virtualenv创建,输入以下命令查看系统是否安装了virtualenv: + +```bash +virtualenv venv +``` + +在Ubuntu系统中,如果没有安装virtualenv,可以使用如下命令安装: + +```bash +sudo apt-get install virtualenv +``` + +在windows系统中,如果没有安装virtualenv,可以使用如下命令安装: + +```bash +pip install virtualenv + +pip install virtualenvwrapper # 这是对virtualenv的封装版本,一定要在virtualenv后安装 +``` + +下载本书代码,并回退到第一章的代码版本: + +```bash +git clone https://github.com/miguelgrinberg/flasky.git +cd flasky +git checkout 1a +``` + +如果出现git timeout,可以使用如下命令: + +```bash +git clone https://gitclone.com/github.com/miguelgrinberg/flasky.git +``` + +接着使用virtualenv在flasky文件夹中创建虚拟环境:(比如说把虚拟环境命名为venv,会在flasky文件夹中创建一个venv文件夹) + +```bash +virtualenv venv +``` + +激活虚拟环境: + +Ubuntu系统: + +```bash +source venv/bin/activate +``` + +Windows系统: + +```bash +venv\Scripts\activate +``` + +虚拟环境被激活之后,命令行前面会出现一个(venv)的前缀,表示当前处于虚拟环境中。 + +当虚拟环境中的工作完成之后,想回到全局Python解释器中,可以在命令行提示符下输入deactivate命令。 + +## 程序基本结构 + +### 初始化 + +Web服务器使用一种名为Web服务器网关接口(Web Server Gateway Interface,WSGI)的协议,把接收自客户端的所有请求都转交给这个对象处理。程序实例就是一个WSGI应用程序,它的构造函数是Flask类的一个实例。 + +```python +from flask import Flask +app = Flask(__name__) +``` + +这个name变量是Python预定义的变量,它的值是模块的名字。在这个例子中,这个值是程序包中的__init__.py文件,因为这个文件是程序包的入口。 + +### 路由和视图函数 + +客户端把请求发给Web服务器,Web服务器再把请求发给Flask程序实例。程序实例需要知道对每个URL请求运行哪些代码,所以保存了一个URL到Python的映射关系。处理URL和函数之间的程序称为路由。 + +```python +@app.route('/') # 装饰器,把修饰的函数注册为路由,这个路由是程序的根路由 +def index(): + return '

Hello World!

' +``` + +修饰器可以使用不同的方法修饰函数的行为,惯常方法是使用修饰器把函数注册为事件的处理程序。 + +像index()这样的函数被称为视图函数(view function),视图函数的返回值可以是包含HTM;的简单字符串,也可以是复杂的表单。 + +直接右键运行程序,flask不启动,原因是没有配置环境变量,需要在命令行中运行程序: + +```bash +export FLASK_APP=hello.py +export FLASK_ENV=development +flask run +# flask run -h ipaddress -p port +``` + +在windows系统中,没有export命令,可以使用如下命令: + +```bash +set FLASK_APP=hello.py +set FLASK_ENV=development +flask run +``` + +还是不行,试一试这个: + +```bash +$env:FLASK_APP="hello.py" +$env:FLASK_ENV="development" +flask run +``` + +在pycharm中可以直接配置configuration来运行。 + +![20230420171028](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230420171028.png) + +hello.py文件中的代码如下: + +```python +from flask import Flask +app = Flask(__name__) + + +@app.route('/') +def index(): + return '

Hello World!

' + + +@app.route('/user/') +def user(name): + return '

Hello, {}!

'.format(name) +``` + +地址输入http://127.0.0.1:5000/user/hugo,可以看到页面显示Hello, hugo! + +地址输入http://127.0.0.1:5000,可以看到页面显示Hello World! diff --git "a/_posts/2023-04-20-Hugging face\344\275\277\347\224\250\345\210\235\344\275\223\351\252\214.md" "b/_posts/2023-04-20-Hugging face\344\275\277\347\224\250\345\210\235\344\275\223\351\252\214.md" new file mode 100644 index 0000000000..d0c5d91182 --- /dev/null +++ "b/_posts/2023-04-20-Hugging face\344\275\277\347\224\250\345\210\235\344\275\223\351\252\214.md" @@ -0,0 +1,224 @@ +--- +layout: post +title: "Hugging face使用初体验" +date: 2023-04-20 +description: "Hugging face使用初体验" +tag: 工具箱 +--- +## 1. 什么是Hugging face + +Hugging Face Hub和 Github 类似,都是Hub(社区)。Hugging Face可以说的上是机器学习界的Github。Hugging Face为用户提供了以下主要功能: + +* [模型仓库(Model Repository)](https://huggingface.co/docs/hub/repositories-getting-started):Git仓库可以让你管理代码版本、开源代码。而模型仓库可以让你管理模型版本、开源模型等。使用方式与Github类似。 +* [模型(Models)](https://huggingface.co/docs/hub/models):Hugging Face为不同的机器学习任务提供了许多[预训练好的机器学习模型](https://huggingface.co/models)供大家使用,这些模型就存储在模型仓库中。 +* [数据集(Dataset)](https://huggingface.co/docs/hub/datasets):Hugging Face上有许多公开数据集。 + +## 2. Hugging face的使用 + +hugging face在NLP领域最出名,其提供的模型大多都是基于Transformer的。 + +你可以用它做以下事情: + +* 直接使用预训练模型进行推理 +* 提供了大量预训练模型可供使用 +* 使用预训练模型进行迁移学习 + +### Transformers安装 + +安装Transformers非常简单,直接安装即可。 + +```shell +pip install transformers +1 +``` + +### 使用Transformers进行推理 + +如果你的任务是一个比较常见的,大概率可以直接使用Transformer提供的Pipeline解决。 + +比如说这,[Pipeline——transformers/v4.21.0/en/main_classes/pipelines](https://huggingface.co/docs/transformers/v4.21.0/en/main_classes/pipelines)API解决,其使用方式非常简单,可以说是直接用即可。 + +```python +from transformers import pipeline + +translator = pipeline("translation_en_to_fr") +print(translator("How old are you?")) +``` + +### 如何搜索模型 + +首先,我们需要到来到官网的[模型模块](https://huggingface.co/models)。 + +其主要包含三部分: + +1. **Filter** : 用于筛选你想要的模型 +2. **模型列表** : 展示了可使用的模型。不带前缀的是官方提供的模型,例如gpt2,而带前缀的是第三方提供的模型。 +3. **搜索框** :你可以通过搜索框按名字搜索模型。 + +点进去某一个模型,主要包括以下内容: + +1. **模型介绍(Model Card)** : 我们可以通过该文档查看该模型都提供了哪些功能,模型的表现等。 +2. **模型文件(Files and versions)** : 从该模块可以下载模型文件,一般包含多种框架的(TF、Pytorch等)模型文件和配置文件等,可以用于离线加载。 +3. **测试模型(Hosted inference API)** : 可以直接通过该模块测试自己的模型。同时Hugging Face也提供了Http API可以调用,这样就不需要本地部署了。详情请参考:https://huggingface.co/docs/api-inference/index +4. **使用该模型的应用(Spaces using …)** :这里展示了使用该模型的应用,可以点进去玩一玩。 +5. **代码样例(Use in Transformers)** :你可以通过该模块直接查看该模型的使用方式,直接拷贝代码到项目里就可以用了。 + +## 部署 + +### 使用API + +![20230420143243](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230420143243.png) + +[bert-base-chinese](https://huggingface.co/bert-base-chinese) + + +```python +import requests + +API_URL = "https://api-inference.huggingface.co/models/bert-base-chinese" +headers = {"Authorization": "Bearer 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"} + +def query(payload): + response = requests.post(API_URL, headers=headers, json=payload) + return response.json() + +output = query({ + "inputs": "The answer to the universe is [MASK].", +}) +``` + +### 使用space + +这里有个文字转成语音的模型,可以直接duplicate this space,他会在自己的空间生成一个Gradio的APP,然后就可以直接使用了。 + +[LINGUISTIC_ACCENTS](https://huggingface.co/spaces/Chandrabhan01/LINGUISTIC_ACCENTS) + +也可以看一下这个repository,里面的file,发现非常简单。 + +![20230420144746](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230420144746.png) + +其中的app.py如下:(这段代码是一个文本转语音的应用程序,支持50多种语言。它使用了Python中的gTTS库和Gradio库。用户可以在文本框中输入要转换为语音的文本,选择目标语言和口音,然后按下提交按钮即可。应用程序还提供了一个选项卡式界面,用户可以在其中将英语句子翻译成其他语言并进行语音合成。) + +```python +import os +from gtts import gTTS +import gradio as gr +from translate import Translator + + +# os.environ['TF_CPP_MIN_LOG_LEVEL']='3' + +# auth_token = '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' # For accessing HuggingFace Facebook Model. + +class Languages: + """ Languages currently supported by the application. """ + # Language code + # lang (string, optional) – The language (IETF language tag) to read the text in. Default is en. + lang = {'Afrikaans': 'af', 'Arabic': 'ar', 'Bulgarian': 'bg', 'Bengali': 'bn', 'Bosnian': 'bs', + 'Catalan': 'ca', 'Czech': 'cs', 'Danish': 'da', 'German': 'de', 'Greek': 'el', 'English': 'en', + 'Spanish': 'es', 'Estonian': 'et', 'Finnish': 'fi', 'French': 'fr', 'Gujarati': 'gu', 'Hindi': 'hi', + 'Croatian': 'hr', 'Hungarian': 'hu', 'Indonesian': 'id', 'Icelandic': 'is', 'Italian': 'it', + 'Hebrew': 'iw', 'Japanese': 'ja', 'Javanese': 'jw', 'Khmer': 'km', 'Kannada': 'kn', 'Korean': 'ko', + 'Latin': 'la', 'Latvian': 'lv', 'Malayalam': 'ml', 'Marathi': 'mr', 'Malay': 'ms', + 'Myanmar (Burmese)': 'my', 'Nepali': 'ne', 'Dutch': 'nl', 'Norwegian': 'no', + 'Polish': 'pl', 'Portuguese': 'pt', 'Romanian': 'ro', 'Russian': 'ru', 'Sinhala': 'si', + 'Slovak': 'sk', 'Albanian': 'sq', 'Serbian': 'sr', 'Sundanese': 'su', 'Swedish': 'sv', + 'Swahili': 'sw', 'Tamil': 'ta', 'Telugu': 'te', 'Thai': 'th', 'Filipino': 'tl', 'Turkish': 'tr', + 'Ukrainian': 'uk', 'Urdu': 'ur', 'Vietnamese': 'vi', 'Chinese (Simplified)': 'zh-CN', + 'Chinese (Mandarin/Taiwan)': 'zh-TW', + 'Chinese (Mandarin)': 'zh'} + + +class TLD: + """ Depending on the top-level domain, gTTS can speak in different accents. """ + + tld = {'English(Australia)': 'com.au', 'English (United Kingdom)': 'co.uk', + 'English (United States)': 'us', 'English (Canada)': 'ca', 'English (India)': 'co.in', + 'English (Ireland)': 'ie', 'English (South Africa)': 'co.za', 'French (Canada)': 'ca', + 'French (France)': 'fr', 'Portuguese (Brazil)': 'com.br', 'Portuguese (Portugal)': 'pt', + 'Spanish (Mexico)': 'com.mx', 'Spanish (Spain)': 'es', 'Spanish (United States)': 'us'} + + +class TTSLayer(): + """ Layer on top of gTTS - providing text to speech for """ + + def __init__(self, text, tld, lang) -> None: + """ [Constructor takes in text, the top-level domain and the language in which the text is : ] """ + self.text = text + self.tld = tld + self.lang = lang + + def tts(self): + """ [Converts the text to speech.] """ + tts = gTTS(text=self.text, tld=TLD.tld[self.tld], lang=Languages.lang[self.lang]) + tts.save('tts.mp3') + with open('tts.mp3') as fp: + return fp.name + + +langs = Languages() +top_level_domain = TLD() + +""" [Utiility Functions] """ + + +def T2TConversion(text, dest): + """ [(Utility Function) : Converts sentence from english to another language ] """ + translator = Translator(to_lang=langs.lang[dest]) + return translator.translate(text) + + +def convert_text(Text, Language, Accent): + """ [(Utility Function) : Performs Text-To-Speech provided language and accent.] """ + tts = TTSLayer(Text, Accent, Language) + return tts.tts() + + +class GRadioInterface: + """ [Class for managing UI for the application.] """ + + def __init__(self, function) -> None: + """ [Interface for packaging GRadio Application] """ + + # Necessary for interface + self._function = function + self._inputs = [ + gr.TextArea(label='The Text to be Converted to Audio'), + gr.Dropdown([key for key, _ in langs.lang.items()], label='Languages Available', ), + gr.Dropdown([key for key, _ in top_level_domain.tld.items()])] + + self.outputs = gr.Audio() + + # Necessary for descriptive content + self.title = 'A Text-To-Speech Converter for Low Resource Languages' + self.description = 'Support over 50 languages !' + self.article = 'How does it work ? Just write a sentence (in target language) in the space provided and select the target language and accent and press submit. That is it. Wait and Enjoy.' + + def start(self): + """ [Launching the interface in a tabbed manner.] """ + + it_1 = gr.Interface(fn=self._function, inputs=self._inputs, outputs=self.outputs, + title=self.title, + description=self.description, + article=self.article) + + it_2 = gr.Interface(fn=T2TConversion, + inputs=[ + gr.Text(label='Write a sentence in English'), + gr.Dropdown([key for key, _ in langs.lang.items()])], + outputs=gr.Text(label='The Converted Text'), + title='Translation from English', + description='Write a sentence in english and convert to other languages for speech synthesis', + article='What if you do not have a sentence in a particular language? Just write the sentence in english and let us do the magic.') + + demo = gr.TabbedInterface([it_1, it_2], ['Speech Synthesis', 'Sentence Translation']) + demo.launch() + + +demo_app = GRadioInterface(function=convert_text) +demo_app.start() +``` + +直接扒拉到本地,然后运行,就可以看到和space里面一样的效果了。 + +![20230420144941](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230420144941.png) \ No newline at end of file diff --git "a/_posts/2023-04-20-\346\211\223\351\200\240\344\270\252\344\272\272github\351\246\226\351\241\265.md" "b/_posts/2023-04-20-\346\211\223\351\200\240\344\270\252\344\272\272github\351\246\226\351\241\265.md" new file mode 100644 index 0000000000..e6f4ae1b76 --- /dev/null +++ "b/_posts/2023-04-20-\346\211\223\351\200\240\344\270\252\344\272\272github\351\246\226\351\241\265.md" @@ -0,0 +1,227 @@ +--- +layout: post + +title: "打造个人github首页" + +date: 2023-04-20 + +description: "今天教大家打造一个炫酷的 GitHub 首页" + +tag: 个人网站 +--- + +今天教大家打造一个炫酷的 GitHub 首页。 + +# 如何实现 + +其实自定义 Github 的首页很简单,我们只需要新建一个仓库名和自己 Github 用户 ID 相同的仓库并且添加一个 README.md 自述文件即可。 + +理论存在,实践开始 +新建一个同名仓库,添加一个自述文件后点击确认。 + +## 1、Typing svg + +用于生成动态生成的、可定制的 SVG。 + +开源项目地址: [https://github.com/DenverCoder1/readme-typing-svg](https://github.com/DenverCoder1/readme-typing-svg) + +在线demo地址: + +[Readme Typing SVG - Demo Site (demolab.com)](https://readme-typing-svg.demolab.com/demo/) + +我自己生成的动态图如下: + +```markdown +[![Typing SVG](https://readme-typing-svg.demolab.com?font=Fira+Code&pause=1000&color=F73262&width=435&lines=Hello%2C+Welcome+to+my+Github+page;%E5%AD%A6%E4%B8%8D%E4%BC%9A%E5%9C%B0%E7%90%83%E6%B5%81%E4%BD%93%E5%8A%9B%E5%AD%A6%E7%9A%84%E7%94%9F%E7%93%9C%E8%9B%8B%E5%AD%90)](https://git.io/typing-svg) +``` + +[![Typing SVG](https://readme-typing-svg.demolab.com?font=Fira+Code&pause=1000&color=F73262&width=435&lines=Hello%2C+Welcome+to+my+Github+page;%E5%AD%A6%E4%B8%8D%E4%BC%9A%E5%9C%B0%E7%90%83%E6%B5%81%E4%BD%93%E5%8A%9B%E5%AD%A6%E7%9A%84%E7%94%9F%E7%93%9C%E8%9B%8B%E5%AD%90)](https://git.io/typing-svg) + +## 2、Shields.io + +开源项目地址: [https://github.com/badges/shields](https://github.com/badges/shields) + +参考项目说明,复制如下代码,便可以在 GitHub 首页中展示您所需要的徽章图标。 + +在线demo地址: + +[Shields.io: Quality metadata badges for open source projects](https://shields.io/#your-badge) + +我自己生成的徽章图标如下:第二个徽标图标外面嵌套了一个链接,点击后会跳转到我的个人网站。 + +```markdown +![](https://img.shields.io/badge/coding-python-blue) + +[![](https://img.shields.io/badge/website-%E9%99%88%E5%90%8C%E5%AD%A6%E4%B8%AA%E4%BA%BA%E7%AB%99-red)](https://chanjeunlam.github.io/) +``` + +![](https://img.shields.io/badge/coding-python-blue) [![](https://img.shields.io/badge/website-%E9%99%88%E5%90%8C%E5%AD%A6%E4%B8%AA%E4%BA%BA%E7%AB%99-red)](https://chanjeunlam.github.io/) + +## 3、Visitor Badge + +开源项目地址: https://github.com/jwenjian/visitor-badge + +这个图标和上面的徽章类似,但作用不同,这个徽标会实时改变,记录此页面被访问的次数。(注意修改 ID) + +```html +
+ +
+``` + +或者用markdown语法: + +```markdown +![](https://visitor-badge.glitch.me/badge?page_id=sun0225SUN) +``` + +## 4、Spotify + +开源项目地址: https://github.com/kittinan/spotify-github-profile + +参考项目说明,复制如下代码(仅供参考),便可以在 GitHub 首页中展示您最近在 spotify 中所听过的音乐。 + +```html + +``` + +## 5、Joke + +开源项目地址: https://github.com/ABSphreak/readme-jokes + +参考项目说明,复制如下代码,便可以在 GitHub 首页中展示一些关于 IT 行业的笑话。 + +```html +Jokes Card +``` + +## 6、GitHub streak + +开源项目地址: https://github.com/DenverCoder1/github-readme-streak-stats + +参考项目说明,复制如下代码,便可以在 GitHub 首页中展示您连续提交代码的天数及贡献总数。(注意修改 ID) + +```html +
+ +
+``` + +或者用markdown语法: + +```markdown +[![GitHub Streak](https://github-readme-streak-stats.herokuapp.com/?user=sun0225SUN&theme=dark&hide_border=true)](https://git.io/streak-stats) +``` + +## 7、Metrics + +工具网站: https://metrics.lecoq.io/ + +在浏览器中打开上方链接,在左侧选择您要展示的内容,复制右边的 Maekdown 代码即可。 + +![Metrics](https://metrics.lecoq.io/ChanJeunlam?template=classic&isocalendar=1&base=header%2C%20activity%2C%20community%2C%20repositories%2C%20metadata&base.indepth=false&base.hireable=false&base.skip=false&isocalendar=false&isocalendar.duration=half-year&config.timezone=Asia%2FShanghai) + +```html +
+ +
+``` + +或者用markdown语法: + +```markdown +![Metrics](https://metrics.lecoq.io/ChanJeunlam?template=classic&isocalendar=1&base=header%2C%20activity%2C%20community%2C%20repositories%2C%20metadata&base.indepth=false&base.hireable=false&base.skip=false&isocalendar=false&isocalendar.duration=half-year&config.timezone=Asia%2FShanghai) + +``` + +## 8、Quotes + +开源项目地址: https://github.com/shravan20/github-readme-quotes + +参考项目说明,复制如下代码,便可以在 GitHub 首页中随机展示一段名人名言。 + +```html + +``` + +## 9、Github Profile Trophy + +开源项目地址: https://github.com/ryo-ma/github-profile-trophy + +参考项目说明,复制如下代码,便可以在 GitHub 首页中展示您的奖杯成就(注意修改 ID) + +```html +
+ +
+``` + +或者用markdown语法: + +```markdown +[![trophy](https://github-profile-trophy.vercel.app/?username=sun0225SUN)](https://github.com/ryo-ma/github-profile-trophy) +``` + +## 10、GitHub 统计卡片 + +开源项目地址: https://github.com/anuraghazra/github-readme-stats  + +参考项目说明,复制如下代码,便可以在 GitHub 首页中展示您的编码数据统计图(注意修改 ID) + +![ChanJeunlam's Most used languages](https://github-readme-stats-git-masterrstaa-rickstaa.vercel.app/api/top-langs/?username=ChanJeunlam&layout=compact&hide_border=true&langs_count=10) + +[![ChanJeunlam's GitHub stats](https://github-readme-stats-git-masterrstaa-rickstaa.vercel.app/api?username=ChanJeunlam)](https://github.com/anuraghazra/github-readme-stats) + + +```html +
+ +
+
+ +
+``` + +或者用markdown语法: + +```markdown +![这里写你的昵称's Most used languages](https://github-readme-stats-git-masterrstaa-rickstaa.vercel.app/api/top-langs/?username=这里替换成你的 GitHub ID&layout=compact&hide_border=true&langs_count=10) + +[![这里写你的昵称's GitHub stats](https://github-readme-stats-git-masterrstaa-rickstaa.vercel.app/api?username=这里替换成你的 GitHub ID)](https://github.com/anuraghazra/github-readme-stats) +``` + +## 11、GitHub Readme Activity Graph + +开源项目地址: https://github.com/Ashutosh00710/github-readme-activity-graph + +参考项目说明,复制如下代码,便可以在 GitHub 首页中展示 GitHub 活动统计图(注意修改 ID) + +```html +
+ +
+``` + +或者用markdown语法: + +```markdown +[![Sunshine's GitHub Activity Graph](https://activity-graph.herokuapp.com/graph?username=sun0225SUN&theme=xcode)](https://github.com/sun0225SUN) +``` + +## 案例仓库 + +好的创意总是能博得人的关注,吸引人的眼球。 + +当我们在逛 GitHub 时,如果遇到了好看的主页,我们便可以直接把他扒下来,为我所用。 + +下面几个仓库收集了一些好看的 GitHub 主页,仅供大家欣赏和参考。 + +开源地址: https://github.com/EddieHubCommunity/awesome-github-profiles + +开源地址: https://github.com/abhisheknaiidu/awesome-github-profile-readme + +开源地址: https://github.com/eryajf/awesome-github-profile-readme-chinese + +# 参考链接 + +[小孙同学](https://blog.sunguoqi.com/archives/github_profile_0) diff --git "a/_posts/2023-04-21-FlaskWeb\345\274\200\345\217\221\345\237\272\344\272\216Python\347\232\204Web\345\272\224\347\224\250\345\274\200\345\217\221\345\256\236\346\210\2302.md" "b/_posts/2023-04-21-FlaskWeb\345\274\200\345\217\221\345\237\272\344\272\216Python\347\232\204Web\345\272\224\347\224\250\345\274\200\345\217\221\345\256\236\346\210\2302.md" new file mode 100644 index 0000000000..70d490c311 --- /dev/null +++ "b/_posts/2023-04-21-FlaskWeb\345\274\200\345\217\221\345\237\272\344\272\216Python\347\232\204Web\345\272\224\347\224\250\345\274\200\345\217\221\345\256\236\346\210\2302.md" @@ -0,0 +1,149 @@ +--- +layout: post +title: "FlaskWeb开发基于Python的Web应用开发实战2" +date: 2023-04-21 +description: "FlaskWeb开发基于Python的Web应用开发实战2" +tag: Python +--- + +## 2.5 请求-响应循环 + +### 2.5.1 程序和请求上下文 + +Flask从客户端收到请求时,要让视图函数能访问一些对象,这样才能处理请求。Flask把这些对象封装在一个叫做请求上下文(request context)的对象中,这个对象在请求被处理前被激活,处理完成后被自动销毁。Flask使用上下文全局变量(context global variable)**g**来存储处理请求期间可能用到的临时变量。这些变量在请求结束后自动清理。 + +举例子: + +```python +from flask import request +@app.route('/') +def index(): + user_agent = request.headers.get('User-Agent') + return '

Your browser is %s

' % user_agent +``` + +在这个视图函数中,我们把request当做全局变量使用。事实上,request不可能是全局变量。多个线程在处理不同客户端发送的不通气请求时,每个线程看到的request对象必然不同。Flask使用上下文让特定的变量在一个线程中全局可访问,与此同时缺不会干扰其他线程。 + + +(多线程Web服务器会创建一个线程池,再从线程池中取出一个线程来处理请求。) + +程序在上下文被推送后,可以在线程中使用current_app和g变量。current_app是程序实例本身,g是一个空对象,用于存储请求期间的临时变量。请求上下文被推送之后,就可以使用request和session变量了。 + +以下演示了程序上下文的使用方法: + + +![20230421083730](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230421083730.png) + + +没有激活程序上下文之前就调用current_app.name会报错,但推送完上下文后就可以调用了。(在程序实例上调用app.app_context()方法,返回一个程序上下文对象。) + + +### 2.5.2 请求调度 + +程序收到客户端发来的请求时,要找到处理该请求的视图函数。Flask使用URL映射(URL map)来实现这一功能。URL映射是一个把URL和视图函数关联起来的字典。Flask在请求被处理前,会把URL映射表保存在程序上下文中。这样,当请求到来时,就可以通过URL映射表找到对应的视图函数。 + +Flask使用app.route修饰器或者非修饰器形式的app.add_url_rule()函数来生成URL映射。 + +检查为hello.py程序生成的URL映射表: + +```shell +(venv) $ python +>>> from hello import app +>>> app.url_map +Map([' (OPTIONS, GET, HEAD) -> static>, + index>, + ' (OPTIONS, GET, HEAD) -> user>]) +``` + +发现有三条Rule,分别是'/static/'、'/'和'/user/'。这三条规则分别对应着三个视图函数。其中,'/'规则对应着index视图函数,'/user/'路由是Flask添加的特殊路由,用于访问静态文件。 + +URL映射中的HEAD/Options/GET是请求方法,由路由器处理。Flask为每个路由都指定了请求方法,这样不同的请求方法发送到相同的URL上时,可以调用不同的视图函数。 + +### 2.5.3 请求钩子 + +为了避免在每个视图函数中都重复编写相同的代码,Flask提供了请求钩子(request hook)机制。请求钩子是在请求被分发到视图函数之前调用的通用函数。 + +Flask支持以下4种钩子。 + +- before_first_request:注册一个函数,在处理第一个请求之前运行。 +- before_request:注册一个函数,在每次请求之前运行。 +- after_request:注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行。 +- teardown_request:注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行。 + +请求钩子函数和视图函数之间共享数据一般使用上下文全局变量g。例如,before_request钩子可以从数据库中加载已登录用户,然后把用户对象保存在g.user中,这样视图函数就可以使用g.user获取用户。 + +### 2.5.4 响应 + +Flask会把视图函数返回的值转换为响应对象,然后把响应对象发送给客户端。 + +视图函数返回的值可以是一个包含响应体、状态码和响应头的元组。 + +有一种名为重定向的特殊响应,它的状态码是302,表示客户端应该重定向到其他位置。重定向的目标位置在响应头的Location字段中指定。 + +## 2.6 Flask扩展 + +扩展是Flask的一个重要组成部分。扩展是对Flask功能的补充,可以为Flask程序添加新的功能。Flask扩展通常以Flask-为前缀,例如Flask-SQLAlchemy。 + +安装Flask-Script扩展: + +```shell +(venv) $ pip install flask-script +``` + +runserver命令可以启动开发服务器: + +```shell +(venv) $ python hello.py runserver +``` + +其中,--host和--port选项分别指定服务器监听的地址和端口。默认情况下,服务器只监听本机的5000端口。如果想让Web服务器监听公共网络接口上的连接,允许同网中的其他计算机连接服务器,可以使用--host 0.0.0.0选项。 + +那个,Web服务器就可以使用http://a.b.c.d:500/网络中的任何以太电脑进行访问,其中a.b.c.d是服务器所在计算机的外网IP地址。 + + + +#### 版本问题 + +Flask以及Jinja2,werkzeug版本过高过低都不行: + +- Werkzeug 0.16.1 +- Flask 1.1.2 +- Jinja2 3.0.2 + + + + +# 第三章 模板 + + +视图函数的作用很明确,也即是生成请求的响应;但是一般来说,请求会改变程序的状态,而这种变化也会在视图函数中产生。 + +例如,用户在网站中注册了一个新账户,用户在表单中输入电子邮箱地址和密码,然后点击提交按钮。服务器接收到用户输入数据的请求,然后Flask把请求分发到注册请求的视图函数,这个视图函数需要访问数据库,添加新用户,然后生成响应回送浏览器。这两个过程分别成为业务逻辑和表现逻辑。 + +把业务逻辑和表现逻辑混在一起,这就会导致视图函数的代码量变得很大,而且难以维护。把表现逻辑移动到模板中,可以让视图函数专注于业务逻辑,这样视图函数的代码量就会变小,而且更容易维护。 + + + +模板是一个包含响应文本的文件,其中包含了一些特殊的标记,这些标记会在模板被渲染时被替换为动态内容。Flask使用Jinja2模板引擎来渲染模板。 + +比较常见的模板标记有: + +- boostrap, 前端框架, 用于快速搭建网站 +- moment.js, 用于在浏览器中处理日期和时间 + +## 第四章 Web表单 + +有些任务很单调,而且要重复操作,比如说生成表单的HTML代码和验证提交的表单数据。 + +Flask-WTF扩展封装了WTForms,并且和Flask框架集成在一起,可以使用Flask-WTF快速实现常见的Web表单功能。 + +### 4.1 跨站请求伪造保护 + +## 第五章 数据库 + +基于关系模型的数据库,这种数据库也被称为SQL数据库,因为他们使用结构化查询语言。 + +最近几年文档数据库和键值对数据库成为了流体的选择,这两种数据库合称为NoSQL数据库。 + + + diff --git "a/_posts/2023-04-25-Linux\344\270\255\345\256\211\350\243\205\345\244\232\344\270\252python\347\216\257\345\242\203.md" "b/_posts/2023-04-25-Linux\344\270\255\345\256\211\350\243\205\345\244\232\344\270\252python\347\216\257\345\242\203.md" new file mode 100644 index 0000000000..3071f581ab --- /dev/null +++ "b/_posts/2023-04-25-Linux\344\270\255\345\256\211\350\243\205\345\244\232\344\270\252python\347\216\257\345\242\203.md" @@ -0,0 +1,106 @@ +--- +layout: post +title: "Linux中安装多个python环境" +date: 2023-04-25 +description: "Linux中安装多个python环境" +tag: Linux +--- + +# 查看现在的python安装位置 + +```shell +hugo@hugo-virtual-machine:~/Python-3.8.0a4$ which python +/home/hugo/anaconda3/bin/python +hugo@hugo-virtual-machine:~/Python-3.8.0a4$ which python3.7 +/usr/bin/python3.7 +hugo@hugo-virtual-machine:~/Python-3.8.0a4$ which python3.10 +/usr/bin/python3.10 +``` + +## 安装Python3.8 + +```shell +sudo apt-get install python3.8 +``` + +安装失败,应该是ppa源的问题,需要更换源 + +```shell +sudo add-apt-repository ppa:deadsnakes/ppa +sudo apt-get update +sudo apt-get install python3.8 +``` + +还是失败,没办法更换源。 + +从源头开始安装,先下载源码包 + +```shell +wget https://www.python.org/ftp/python/3.8.0/Python-3.8.0a4.tgz +``` + +解压 + +```shell +tar -zxvf Python-3.8.0a4.tgz +``` + +进入解压后的目录 + +```shell +cd Python-3.8.0a4 +``` + +配置 + +```shell +./configure --enable-optimizations +./configure --prefix=/usr/local/python3.8 +``` +./configure这段代码是用来检查系统环境是否满足编译安装Python的要求,如果不满足,会提示你缺少哪些依赖包,需要你自己去安装,安装完成后再次执行./configure,直到没有提示信息,这时候就可以执行make命令了。 + +--prefix=/usr/local/python3.8这段代码是用来指定Python的安装路径,如果不指定,默认安装在/usr/local/bin目录下。 + +--enable-optimizations这段代码是用来开启Python的优化编译,这样可以提高Python的运行效率。 + +编译 + +```shell +make -j 8 +``` + +-j 8表示使用8个线程进行编译,这样可以加快编译速度,如果你的电脑是双核的,可以使用-j 2,如果是四核的,可以使用-j 4,以此类推。 + +查看核心数 + +```shell +hugo@hugo-virtual-machine:~/Python-3.8.0a4$ cat /proc/cpuinfo | grep "processor" | wc -l +4 +``` + +安装 + +```shell +sudo make install +``` + +编译安装同时进行,可以使用 + +```shell +make -j 4 && sudo make install +``` + +安装完成后,查看安装位置 + +```shell +hugo@hugo-virtual-machine:~/Python-3.8.0a4$ which python3.8 +/usr/local/python3.8/bin/python3.8 +``` + +好像在这之前,需要安装编译 Python 所必要的软件包: + +```shell +sudo apt install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev wget +sudo apt update +``` + diff --git "a/_posts/2023-04-27-\345\273\272\347\253\213\346\267\261\345\272\246\345\255\246\344\271\240Ubuntu\345\256\236\351\252\214\347\216\257\345\242\203.md" "b/_posts/2023-04-27-\345\273\272\347\253\213\346\267\261\345\272\246\345\255\246\344\271\240Ubuntu\345\256\236\351\252\214\347\216\257\345\242\203.md" new file mode 100644 index 0000000000..0347158ab7 --- /dev/null +++ "b/_posts/2023-04-27-\345\273\272\347\253\213\346\267\261\345\272\246\345\255\246\344\271\240Ubuntu\345\256\236\351\252\214\347\216\257\345\242\203.md" @@ -0,0 +1,127 @@ +--- +layout: post +title: 建立深度学习Ubuntu实验环境 +date: 2023-04-27 +description: "建立深度学习Ubuntu实验环境" +tag: Python +--- +# 建立深度学习Ubuntu实验环境 + +## 安装nvidia显卡驱动 + +[NVIDIA 驱动程序下载](https://www.nvidia.cn/Download/index.aspx?lang=cn) + +```shell +sudo ubuntu-drivers autoinstall +nvidia-smi +#用这种方式安装驱动后,不用特意去禁用nouveau,官方的run文件也不用再去下载。 +#查看显卡型号命令 +lspci | grep -i nvidia +# 给驱动run文件赋予执行权限 +sudo chmod a+x NVIDIA-Linux-x86_64-*.run +# 安装 +./NVIDIA-Linux-x86_64-*.run +``` + + +## 安装TensorFlow +```shell +hugo@hugo-virtual-machine:~$ mkdir my_tensorflow +hugo@hugo-virtual-machine:~$ cd my_tensorflow/ +hugo@hugo-virtual-machine:~/my_tensorflow$ python3.10 -m venv vision +hugo@hugo-virtual-machine:~/my_tensorflow$ source venv/bin/activate +(vision) hugo@hugo-virtual-machine:~/my_tensorflow$ pip install --upgrade pip +(vision) hugo@hugo-virtual-machine:~/my_tensorflow$ pip install tensorflow tensorflow-gpu +``` + +python -m表示运行一个模块,venv表示虚拟环境,vision表示虚拟环境的名字,source表示激活虚拟环境,pip install --upgrade pip表示更新pip,pip install tensorflow tensorflow-gpu表示安装tensorflow和tensorflow-gpu。 + +发现tensorflow-gpu安装失败,原因是没有安装CUDA,所以需要先安装CUDA。 + +## 安装CUDA + +打开[https://developer.nvidia.com/cuda-toolkit-archive](https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=x86_64&Distribution=Ubuntu&target_version=22.04&target_type=deb_local),选择CUDA Toolkit 12.1,选择Linux,选择x86_64,选择Ubuntu,选择22.04,选择runfile(local),安装向导如下。 + +```shell +wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-ubuntu2204.pin +sudo mv cuda-ubuntu2204.pin /etc/apt/preferences.d/cuda-repository-pin-600 +wget https://developer.download.nvidia.com/compute/cuda/12.1.1/local_installers/cuda-repo-ubuntu2204-12-1-local_12.1.1-530.30.02-1_amd64.deb +sudo dpkg -i cuda-repo-ubuntu2204-12-1-local_12.1.1-530.30.02-1_amd64.deb +sudo cp /var/cuda-repo-ubuntu2204-12-1-local/cuda-*-keyring.gpg /usr/share/keyrings/ +sudo apt-get update +sudo apt-get -y install cuda +``` + +## 安装CUDA Deep Neural Network (cuDNN) + +打开[https://developer.nvidia.com/rdp/cudnn-archive](https://developer.nvidia.com/cudnn),选择cuDNN 8.2.4 for CUDA 12.1,选择cuDNN Library for Linux,选择x86_64,下载cuDNN Library for Linux (x86_64)。 + +```shell + + +## 安装Keras + +```shell +git clone https://github.com/keras-team/keras.git +cd keras +python setup.py install +``` + +现在可以尝试运行一下一个Keras脚本,比如说MNIST的例子。 + +```shell +python examples/mnist_mlp.py +``` + +可以在~/.keras/heras.json找到Keras的配置文件,可以修改backend(后端)为tensorflow或者theano或者cntk. + +运行脚本时候,可以在shell窗口中监控GPU利用率。 + +```shell +watch -n 5 NVIDIA-smi -a --display=utilization +``` + +watch表示监控,-n 5表示每5秒刷新一次,NVIDIA-smi表示NVIDIA System Management Interface,-a表示显示所有信息,--display=utilization表示显示GPU利用率。 + +```shell +{ + "image_data_format": "channels_last", + "epsilon": 1e-07, + "floatx": "float32", + "backend": "tensorflow" +} +``` + +## 安装OpenCV + +```shell +sudo apt-get install libopencv-dev python-opencv +``` + +## 安装Jupyter + +```shell +pip install jupyter +``` + +## 映射远端端口到本地 + + + **第一步** ,在远程Terminal中启动[Jupyter](https://so.csdn.net/so/search?q=Jupyter&spm=1001.2101.3001.7020) Notebook: + +```python +jupyter notebook --no-browser --port=8889 +``` + + +```shell +ssh -L 8888:localhost:8888 remote_user@remote_host +``` + +这段表示将远端的8888端口映射到本地的8888端口。remote_user表示远端的用户名,remote_host表示远端的主机名或者IP地址。 + +然后在本地运行jupyter notebook,就可以在本地访问远端的jupyter notebook了。 + +```shell +jupyter notebook +``` diff --git "a/_posts/2023-04-29-\346\234\215\345\212\241\345\231\250\351\205\215\347\275\256\345\237\272\347\241\200\346\225\231\347\250\213.md" "b/_posts/2023-04-29-\346\234\215\345\212\241\345\231\250\351\205\215\347\275\256\345\237\272\347\241\200\346\225\231\347\250\213.md" new file mode 100644 index 0000000000..cf86a7da83 --- /dev/null +++ "b/_posts/2023-04-29-\346\234\215\345\212\241\345\231\250\351\205\215\347\275\256\345\237\272\347\241\200\346\225\231\347\250\213.md" @@ -0,0 +1,158 @@ +--- +layout: post +title: 服务器配置基础教程 +date: 2023-04-29 +description: "服务器配置基础教程" +tag: 服务器配置 +--- + +# 配置网络服务 + +先测试本地lo网络是否通畅: + +```shell +ping 127.0.0.0 +``` + +## 手机USB网络共享网络 + +通过手机USB网络共享网络给服务器: + +```shell +ifconfig # 终端中查看现有网络接口,或者使用ip addr,如果出现类似usb0的接口,说明手机已经连接上了 +dhclient usb0 # 申请IP,或者直接dhclient +ping www.baidu.com # 测试网络是否通畅 +``` + +查看硬件信息,使用[lspci](https://linux.cn/article-13793-1.html): + +```shell +lspci -v # -v参数可以查看详细信息 +``` + +在这里会显示有线网卡和无线网卡的信息,其中无线网卡的名字为wlan**,有线网卡的名字为Ethernet**。 + + + +## 命令行连接wifi + +```shell + +# 查看wifi接口 +iwconfig +# or +nmcli dev wifi + +# 连接wifi +nmcli dev wifi connect "wifi名" password "密码" +``` + + +# 挂载U盘 + +## 手动挂载 + +```shell +# 查看U盘 +df -h +# 挂载U盘 +mount /dev/sdc1 /home +``` + +挂载路径不能是系统路径,否则会清空单前系统数据或者无法启动机器。如/root,/usr等。 + +## 通过UUID自动挂载 + + +```shell +lsblk -f # 查看U盘UUID +vim /etc/fstab # 添加一行:UUID=xxx /home vfat/ext4 defaults 0 0 +``` + + +# 更新源 + +查看Ubuntu系统版本: + +```shell +lsb_release -a +``` + +查看操作系统架构: + +```shell +uname -a +``` + +uname -a命令的输出结果中,x86_64表示64位系统,i386表示32位系统。 + +或者: + +```shell +dpkg --print-architecture +``` + +看看是amd64还是aarch64。 + + +更换源: + +```shell +cp /etc/apt/sources.list /etc/apt/sources.list.bak +vim /etc/apt/sources.list +``` + +以[清华源](https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu/)为例,将以下内容复制到sources.list中:(1对应ubuntu;2对应ubuntu-ports) + +1. 对于32/64位x86架构处理器,22.04LTS: + +```shell +# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释 +deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted universe multiverse +# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted universe multiverse +deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse +# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse +deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse +# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse + +# deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-security main restricted universe multiverse +# # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-security main restricted universe multiverse + +deb http://security.ubuntu.com/ubuntu/ jammy-security main restricted universe multiverse +# deb-src http://security.ubuntu.com/ubuntu/ jammy-security main restricted universe multiverse + +# 预发布软件源,不建议启用 +# deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-proposed main restricted universe multiverse +# # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-proposed main restricted universe multiverse +``` + +2. 对于 ARM(arm64, armhf)、PowerPC(ppc64el)、RISC-V(riscv64) 和 S390x 等架构的Ubuntu系统,22.04LTS: + +```shell +# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释 +deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ jammy main restricted universe multiverse +# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ jammy main restricted universe multiverse +deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ jammy-updates main restricted universe multiverse +# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ jammy-updates main restricted universe multiverse +deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ jammy-backports main restricted universe multiverse +# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ jammy-backports main restricted universe multiverse + +# deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ jammy-security main restricted universe multiverse +# # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ jammy-security main restricted universe multiverse + +deb http://ports.ubuntu.com/ubuntu-ports/ jammy-security main restricted universe multiverse +# deb-src http://ports.ubuntu.com/ubuntu-ports/ jammy-security main restricted universe multiverse + +# 预发布软件源,不建议启用 +# deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ jammy-proposed main restricted universe multiverse +# # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ jammy-proposed main restricted universe multiverse +``` + +更新软件和源: + +```shell +sudo apt-get update +sudo apt-get upgrade +``` + +update是更新源,upgrade是更新软件。 diff --git "a/_posts/2023-05-04-\344\275\277\347\224\250ChatGPT\345\210\233\345\273\272\347\224\250\346\210\267\347\252\227\344\275\223.md" "b/_posts/2023-05-04-\344\275\277\347\224\250ChatGPT\345\210\233\345\273\272\347\224\250\346\210\267\347\252\227\344\275\223.md" new file mode 100644 index 0000000000..a6bccec45f --- /dev/null +++ "b/_posts/2023-05-04-\344\275\277\347\224\250ChatGPT\345\210\233\345\273\272\347\224\250\346\210\267\347\252\227\344\275\223.md" @@ -0,0 +1,179 @@ +--- +layout: post +title: 使用ChatGPT创建用户窗体 +date: 2023-05-04 +description: "使用ChatGPT创建用户窗体" +tag: VBA +--- + +# 为啥整活 + +## 使用chatGBT创建vba模块代码 + +上次使用chatGBT,学会了基本的VBA使用方法,也就是如何创建一个模块代码,然后运行代码,批量修改Excel表格,下面是使用流程。 + + +提供给chatGBT的prompt文本如下: + +```txt +vba,第一行为行名,分别为“编号”,“买入价格”,“卖出价格”,以及“利润”。编号这一列从0到90,买入价格这一列填入0-100的随机数,卖出价格是买入价格乘以(1到1.5)的随机数,利润是(卖出价格-买入价格)/买入价格 +``` + +然后chatGBT会返回如下的response文本: + +```txt +下面是一个VBA代码示例,用于在Excel工作表中填充编号、买入价格、卖出价格和利润。代码的解释已经在注释中提供,请按照以下步骤操作: + +打开Excel工作表,按下Alt+F11键打开VBA编辑器。在编辑器中,选择“插入”菜单,然后选择“模块”。在新模块中输入以下代码: +(代码见下文) +运行此代码,请返回Excel工作表,按下Alt+F8键打开宏对话框,然后选择“FillData”,并单击“运行”按钮。此时,代码将开始执行,并将编号、买入价格、卖出价格和利润填充到工作表中。 +请注意,此代码使用了一个For循环来遍历从第二行到第91行的每一行。在每一行中,我们使用Rnd函数生成随机数,并按照要求计算买入价格、卖出价格和利润,并将它们填充到工作表中的相应单元格中。 +``` + +vba代码如下: + +```vba +Sub FillData() + Dim row As Integer, col As Integer + '写入行名 + Cells(1, 1).Value = "编号" + Cells(1, 2).Value = "买入价格" + Cells(1, 3).Value = "卖出价格" + Cells(1, 4).Value = "利润" + + '循环填充数据 + For row = 2 To 91 + '填充编号 + Cells(row, 1).Value = row - 2 + + '填充买入价格 + Cells(row, 2).Value = Int((100 - 0 + 1) * Rnd + 0) + + '填充卖出价格 + Cells(row, 3).Value = Cells(row, 2).Value * (1 + Rnd * 0.5) + + '填充利润 + Cells(row, 4).Value = (Cells(row, 3).Value - Cells(row, 2).Value) / Cells(row, 2).Value + Next row +End Sub +``` + +看不懂代码怎么办? + +继续问: + +```txt +Next row,Dim row As Integer, col As Integer是啥意思呀 +``` + +chatGBT会返回: + +```txt +在VBA中,使用关键字“Dim”来声明变量,例如“Dim row As Integer”和“Dim col As Integer”声明了两个整型变量row和col。这意味着在程序中,我们可以使用这些变量来存储整数类型的值。 + +在这个例子中,“row”和“col”是循环变量,在代码中使用了For循环。在VBA中,For循环用于重复执行一组语句,直到满足指定的条件。例如,在这个例子中,For循环用于重复执行从第二行到第91行的代码,每次循环时变量“row”的值递增1,直到达到指定的上限。 + +所以,“Dim row As Integer”和“Dim col As Integer”声明了两个整型变量,而“Next row”表示结束了当前循环,同时将“row”的值加1,然后开始下一个循环迭代。 +``` + +## 为啥要创建用户窗体 +这次就想着能不能用VBA创建一个用户窗体,然后在用户窗体中输入数据,然后在Excel表格中显示出来。 + +主要是这样可以让用户更加方便的使用Excel表格,而不用去修改VBA代码。 + +## 如何创建用户窗体 + +丢给chatGBT的prompt文本如下: + +```txt +你好,我想用vba创建一个交互的窗口,有一句提示(请输入一个整数),比如说用户输入100;然后提示你想要覆盖的区域是哪些,比如说用户想要覆盖A1:F100.最后,excel中A1:F100的单元格填入1到100的随机数。 +``` + +chatGBT返回的response文本如下: + +```txt +下面是一个简单的VBA代码示例,用于创建一个交互式窗口,提示用户输入一个整数和要覆盖的区域,并在Excel工作表中填充1到100的随机数。请按照以下步骤操作: + +1. 打开Excel工作表,按下Alt+F11键打开VBA编辑器。在编辑器中,选择“插入”菜单,然后选择“用户窗体”。在新用户窗体中,添加一个标签控件(Label)、两个文本框控件(TextBox),以及两个按钮控件(Button)。请参考下面的截图: +2. 双击标签控件,将其文本更改为“请输入一个整数:”,并将其位置设置为(10, 10)。然后,双击第一个文本框控件,将其名称更改为“txtNumber”,并将其位置设置为(150, 10)。重复此过程,将第二个文本框控件的名称更改为“txtRange”,并将其位置设置为(150, 40)。 + +3. 双击第一个按钮控件,将其名称更改为“btnOK”,并将其文本更改为“确定”。然后,将其位置设置为(50, 80)。重复此过程,将第二个按钮控件的名称更改为“btnCancel”,并将其文本更改为“取消”,并将其位置设置为(150, 80)。 + +4. 双击“确定”按钮控件,将其代码编辑如下: +(代码块1) +5. 双击“取消”按钮控件,将其代码编辑如下: +(代码块2) +6. 在代码模块中添加以下过程,用于生成1到100的随机数并填充指定的单元格范围: +(代码块3) +7. 在工作表中,插入一个按钮或菜单,用于打开用户窗体。然后,将此按钮或菜单的代码编辑如下: +(代码块4) + +8. 运行此代码,请返回Excel工作表,并单击添加的按钮或菜单。此时,将弹出一个交互式窗口,提示用户输入一个整数和要覆盖的单元格范围。在用户输入这些信息后,单击“确定”按钮,将在指定的单元格范围中填充1到100的随机数。 +请注意,此代码使用了UserForm控件来创建一个交互式窗口,其中包含标签、文本框和按钮控件。在点击“确定”按钮时,我们将验证用户输入并将其转换为相应的数据类型,然后使用FillRandomNumbers过程填充随机数。在单击“取消”按钮时,我们只是关闭窗口。 +``` + +代码块1: + +```vb +Private Sub btnOK_Click() + Dim num As Integer + Dim rng As Range + '验证用户输入 + If Not IsNumeric(txtNumber.Value) Then + MsgBox "请输入一个整数。", vbExclamation, "错误" + txtNumber.SetFocus + Exit Sub + End If + If Not IsNumeric(txtRange.Value) Then + MsgBox "请输入一个有效的单元格范围。", vbExclamation, "错误" + txtRange.SetFocus + Exit Sub + End If + '转换用户输入 + num = CInt(txtNumber.Value) + Set rng = Range(txtRange.Value) + '填充随机数 + FillRandomNumbers rng, num + '关闭窗口 + Unload Me +End Sub +``` + +代码块2: + +```vb +Private Sub btnCancel_Click() + '关闭窗口 + Unload Me +End Sub +``` + +代码块3: + +```vb +Sub FillRandomNumbers(rng As Range, num As Integer) + Dim row As Integer, col As Integer + For row = 1 To rng.Rows.Count + For col = 1 To rng.Columns.Count + rng.Cells(row, col).Value = Int((num - 1 + 1) * Rnd + 1) + Next col + Next row +End Sub +``` + +代码块4: + +```vb +Private Sub btnOpenForm_Click() + UserForm1.Show +End Sub +``` + +看不懂代码意思,先操作一下: + + + +问chatGBT问题: + + + diff --git "a/_posts/2023-05-05-Ubuntu\345\233\272\345\256\232IP\345\234\260\345\235\200.md" "b/_posts/2023-05-05-Ubuntu\345\233\272\345\256\232IP\345\234\260\345\235\200.md" new file mode 100644 index 0000000000..15f32b9c98 --- /dev/null +++ "b/_posts/2023-05-05-Ubuntu\345\233\272\345\256\232IP\345\234\260\345\235\200.md" @@ -0,0 +1,141 @@ +--- +layout: post +title: Ubuntu固定IP地址 +date: 2023-05-05 +description: "Ubuntu固定IP地址" +tag: Linux +--- +## Ubuntu固定IP地址作用 + +首先,查看IP地址的命令是: + +```bash +ip addr show +``` + +要注意子网掩码是21还是24.比如说172.17.23.45/21这里的21。 + +21 是 CIDR(Classless Inter-Domain Routing)表示法中的子网掩码位数,它指示了 IP 地址中网络部分和主机部分的分界线。 + +在这种情况下,172.17.23.45/21 表示 IP 地址 172.17.23.45,其中前面的 21 位(即前三个字节和第四个字节的前 5 位)表示网络部分,后面的 11 位(即第四个字节的后 3 位)表示主机部分。换句话说,这意味着这个 IP 地址是在一个使用子网掩码 255.255.248.0 的 /21 网络中。这个网络中可以容纳 2^(32-21) - 2 = 2046 个主机。 + +在 netplan 中,使用 CIDR 表示法来指定 IP 地址和子网掩码的组合,这样更加简洁和方便。例如,/21 子网掩码可以表示为 255.255.248.0。 + +在Ubuntu中,为无线网卡固定IP地址可以实现以下作用: + +1. 确保无线网卡的IP地址不会随机变化,使得其他设备可以通过该IP地址访问该设备。 +2. 确保无线网卡的IP地址与其他设备的IP地址不会冲突,避免网络出现问题。 + +## Ubuntu固定IP地址的解决途径 + +为无线网卡固定IP地址的解决途径如下: + +在 /etc/netplan/ 目录下创建一个YAML格式的配置文件,例如 01-network-manager-all.yaml,然后将以下内容添加到该文件中: + +```yaml +network: + version: 2 + renderer: NetworkManager + wifis: + wlx90de80a1d942: + dhcp4: no + addresses: [172.17.23.45/24] # 将“192.168.0.10”修改为需要设置的IP地址 + gateway4: 172.17.16.1 # 将“192.168.0.1”修改为本地路由器的IP地址 + nameservers: + addresses: [8.8.8.8, 8.8.4.4] # 根据需要修改DNS服务器的地址 + access-points: + "your_SSID": + password: "your_password" +``` + +保存文件后,执行以下命令使配置生效: + +```bash +sudo netplan apply +``` + + +### 注意事项 + +renderer: NetworkManager这句话是指在 Linux 系统中使用 NetworkManager 作为网络渲染器(renderer),除了networkmanager还有如下渲染器: + + +1. systemd-networkd:systemd-networkd 是由 systemd 守护进程提供的网络管理服务。它可以自动检测和配置网络接口、连接到无线网络、管理 VLAN 和 PPP 连接等。 +2. ifupdown:ifupdown 是一个传统的网络管理工具,可以在 Debian 和 Ubuntu 等 Linux 发行版中使用。它通过读取和解释 `/etc/network/interfaces` 文件来管理网络连接。 +3. Connman:Connman 是一个轻量级的网络管理器,适用于嵌入式系统和移动设备上。它支持无线和有线网络,以及 PPP 和 VPN 连接。 + +选择哪种网络渲染器取决于应用程序的具体需求和所在的 Linux 发行版。例如,对于桌面 Linux 发行版,NetworkManager 是最常见的网络管理器之一,而在嵌入式系统中,Connman 可能更适合,因为它具有较小的内存占用和更快的启动速度。 + + +如果使用的是 systemd-based 的 Linux 发行版(如 Ubuntu、Debian、CentOS、Fedora 等),可以使用以下命令检查正在使用的网络渲染器类型: + + +```shell +$ systemctl status systemd-networkd +``` + +如果输出中显示 `systemd-networkd.service`,则表示正在使用 systemd-networkd 渲染器。如果没有显示该服务,则可能使用其他网络渲染器(如 NetworkManager 或 ifupdown)或者根本没有启用网络渲染器。 + + +这里的**wlx90de80a1d942**是无线网卡的接口名称,根据实际情况可能需要修改。 + +DNS服务器的地址是用于解析域名的服务器地址,可以根据实际需要进行配置。在上面的示例中,我使用了 Google 的公共 DNS 服务器地址 8.8.8.8 和 8.8.4.4,你可以根据自己的需求修改成其他 DNS 服务器的地址。 + +本地路由器的IP地址是指连接到你本地网络的路由器的IP地址。一般情况下,你可以在路由器的管理界面中查看该地址。如果你不确定本地路由器的IP地址是什么,可以在命令行中输入以下命令,查看你的计算机的默认路由器的IP地址: + +```bash +ip route | grep default +``` + +ip route 命令的输出结果中,以 default 开头的一行就是你的计算机的默认路由器的IP地址。 + +输出结果如下: + +```bash +default via 172.17.16.1 dev wlx90de80a1d942 proto dhcp metric 600 +``` + +proto dhcp 表示该IP地址是通过 DHCP 协议获取的,metric 600 表示该IP地址的优先级为 600。 + +或者使用如下命令也可以查看默认路由器的IP地址: + +```bash +nmcli device show wlx90de80a1d942 | grep IP4.GATEWAY +``` + +输出如下: + +```text +IP4.GATEWAY: 172.17.16.1 +``` + +或者使用如下命令,也可以查看默认路由器的IP地址: + +```bash +route -n +``` + +route,-n表示不解析域名,输出结果如下: + +```bash +Kernel IP routing table +Destination Gateway Genmask Flags Metric Ref Use Iface +0.0.0.0 172.17.16.1 0.0.0.0 UG 600 0 0 wlx90de80a761a3 +172.17.16.0 0.0.0.0 255.255.248.0 U 600 0 0 wlx90de80a761a3 +``` + +如果使用的是静态IP地址,而且没有设置 DNS 服务器地址,那么你可能会遇到无法解析域名的问题。因此,建议在配置静态IP地址时,至少设置一个可用的 DNS 服务器地址。 + +/24 是子网掩码的一种表示方法,也叫做 CIDR 表示法。CIDR 表示法用于指定网络地址的长度,表示子网掩码中前面的连续的1的个数,如 /24 表示子网掩码中前24位是1,后8位是0。 + +对于这段代码,addresses 指定了无线网卡的IP地址和子网掩码。172.17.19.231/24 表示该IP地址的前24位是网络地址,后8位是主机地址,即该IP地址属于一个子网,子网掩码是 255.255.255.0,可以容纳256个主机。 + +因此,这里的 /24 表示子网掩码的前24位是1,后8位是0,即子网掩码是 255.255.255.0。 + +## 查看修改是否生效 + +执行以下命令查看无线网卡的IP地址是否已经修改为 + +```bash + ip addr show wlx90de80a1d942 +``` diff --git "a/_posts/2023-05-08-\345\216\213\345\212\233\346\265\213\350\257\225\347\256\200\346\264\201.md" "b/_posts/2023-05-08-\345\216\213\345\212\233\346\265\213\350\257\225\347\256\200\346\264\201.md" new file mode 100644 index 0000000000..3bdafe1d0c --- /dev/null +++ "b/_posts/2023-05-08-\345\216\213\345\212\233\346\265\213\350\257\225\347\256\200\346\264\201.md" @@ -0,0 +1,96 @@ +--- +layout: post +title: 压力测试简介 +date: 2023-05-08 +description: "压力测试简介" +tag: 自动化测试 +--- +## 什么是压力测试 + +单接口压测是为了能够在开发阶段对单个接口进行性能测试,快速了解接口的承载能力、发现性能瓶颈,在开发早期就能发现问题,消除性能风险。 + +另外,当业务在生产环境遇到实际接口请求瓶颈时,通过模拟真实的并发环境,也是一种快速寻找问题的方式。 + +## 如何衡量接口性能 + +CPU、显卡好,能承受的IO是高的;网卡大,能承受的流量很大。性能与硬件是有关系的。 + +* 衡量性能的指标有: + * 并发数 + * TPS + * MRT、RT90值、RT99值 (RT:Response time) + * 资源使用率(CPU、内存、网络、磁盘) + +### 性能指标 + +并发数:系统同时处理的请求数量。 +吞吐量:每秒钟成功处理的事务数/请求数。吞吐量的衡量指标包括: + +* TPS: Transactions Per Second,每秒钟系统能够处理的事务数,即每秒钟系统能够处理的请求数量。 +* IOPS: Input/Output Operations Per Second,每秒钟系统能够处理的输入/输出请求数量。 +* QPS: Queries Per Second,每秒钟系统能够处理的查询请求数量。 +* BPS: Bytes Per Second,每秒钟系统能够处理的字节数量。 + +### 响应时间 + +RT:Response Time,响应时间,即从请求发出到收到响应的时间。RT的衡量指标包括: + +* MRT: Mean Response Time,平均响应时间,即所有请求的响应时间的平均值。 +* RT90: Response Time 90,90%请求的响应时间小于RT90。 +* RT99: Response Time 99,99%请求的响应时间小于RT99。 +* STD: Standard Deviation,标准差,即所有请求的响应时间的标准差。STD越小,系统的响应时间越稳定。 +* MAX 最大值,长尾时间。 + +### 资源使用率 + +* 系统资源:CPU/内存/磁盘IO/网络IO +* 应用资源:队列/锁/GC(垃圾回收)/ + +## 性能测试流程 + +### 压测结果的影响因素 + +高并发结果很容易受外界因素影响,压测时需要尽量减少外界因素影响。 + +影响压测结果外界因素有 **本机句柄数限制,dns解析速度,网络质量,服务端连接数限制等等** 。例如使用1w并发,很容易出现超过本机最大句柄数限制(一般最大限制1024),超过句柄数限制的请求会因为句柄数受限导致连接失败。 + +因此选择 **合适并发数对测试接口性能非常重要** ,并非并发数越大越好。 + +实际实践中建议并发数先在10,100,500,1000左右分别测试下,如果失败率小于1%,再考虑逐步增加并发数量。 **只有增加并发每秒请求数量能持续增加才是健康的使用方式。当并发数增加到一定数量后,接口响应能力不再增加或者错误数变高,这时候再增加并发量是无效的** 。 + + +## 测试阶段 + +![20230508112611](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230508112611.png) + +a点:性能期望值 + +b点:高于期望,系统资源处于临界点 +c点:高于期望,性能处于拐点,系统能承受的最大的TPS值 +d点:系统资源出现瓶颈,性能下降 + +* 性能测试:通常在a点和b点之间进行 +* 负载测试:b点到c点 +* 压力测试:b点到d点,压倒最高点c,再加大线程,TPS反而下降,得出的c点即为系统能承受的最大的TPS值 +* 稳定性测试:a-b,不要超过c(超过c是没有意义的) + +## 测试指标 + +做好监控:把一切监控起来 +* 性能指标:TPS、MRT、RT90值、失败率 +* 资源监控: +监控点:CPU、内存、网络、磁盘 +监控机器:压测机器、nginx、应用服务、数据库、缓存、消息队列 +* 日志监控:检查错误、异常,及时发现错误 + + +## 性能测试工具Apipost + +![20230508113642](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230508113642.png) + +![20230508113728](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230508113728.png) + +## 参考链接 + +1. [发现一款后端程序员必备的压测工具,十分钟就能上手,yyds - 掘金 (juejin.cn)](https://juejin.cn/post/7198182873366249533) +2. [8-26接口压力测试-2性能测试入门 - singleSpace - 博客园 (cnblogs.com)](https://www.cnblogs.com/csj2018/p/11722867.html) diff --git "a/_posts/2023-05-22-\344\273\216\346\234\215\345\212\241\345\231\250\344\270\212\346\211\271\351\207\217\346\213\267\350\264\235\346\214\207\345\256\232\346\227\245\346\234\237\347\232\204\346\226\207\344\273\266.md" "b/_posts/2023-05-22-\344\273\216\346\234\215\345\212\241\345\231\250\344\270\212\346\211\271\351\207\217\346\213\267\350\264\235\346\214\207\345\256\232\346\227\245\346\234\237\347\232\204\346\226\207\344\273\266.md" new file mode 100644 index 0000000000..81ebb4ccbb --- /dev/null +++ "b/_posts/2023-05-22-\344\273\216\346\234\215\345\212\241\345\231\250\344\270\212\346\211\271\351\207\217\346\213\267\350\264\235\346\214\207\345\256\232\346\227\245\346\234\237\347\232\204\346\226\207\344\273\266.md" @@ -0,0 +1,149 @@ +--- +layout: post +title: 从服务器上批量拷贝指定日期的文件 +date: 2023-05-22 +description: "从服务器上批量拷贝指定日期的文件" +tag: Linux +--- +## 下载05月17日到05月19日的文件 + +用户有一个目录,里面有很多文件,文件名包含日期,如 `20230517`,`20230518`,`20230519`,如/data/20230517/*,/data/20230522/*,现在需要将05月17日到19日的这些文件拷贝到本地。 + +可以通过 `find`命令来查找指定日期的文件,然后通过 `cp`命令来拷贝文件到新的目录。前提是用户在服务器上有写权限。 + +```sh +new_root="/home/output" + +# 日期列表 +dates=("0517" "0518" "0519") + +for date in "${dates[@]}"; do + # 寻找指定日期的文件 + for file in $(find . -name "*$date*" -type f -print); do + # 在这里对每个文件进行操作,比如打印文件名 + echo $file + + # 提取文件名中的目录部分 + dir=$(dirname "$file") + echo $dir + + # 提取目录中的最后一个部分作为名称 + name=$(basename "$file") + echo $name + + # 构建新的目录结构 + new_path=${dir#./} + echo $new_path + + new_dir="$new_root/$new_path/$name" + echo $new_dir + # 如果新目录不存在,则创建它 + mkdir -p "$new_dir" + # 复制文件到新目录 + cp "$file" "$new_dir" + done +done +``` + +## 没有写权限 + +如果用户没有写权限,可以通过 `scp`命令来拷贝文件到本地。 + +任务要求:在data文件夹下面,有大概十五个变量命名的文件夹,每个文件夹下面有以日期命名的文件,如/data/height/h5/0517,/data/IMU/0518,...,现在需要把他们按照服务器上的文件路径结构拷贝到本地。 + +本地是window系统,服务器是linux系统,而且用户在服务器上没有写的权限,意味着没办法上传公钥到服务器上,只能通过密码登录。 + +思路: + +一、通过ssh登录服务器,然后通过find命令查找指定日期的文件, +二、打印结果,并且通过subprocess.getoutput()函数获取结果。 +三、通过split()函数将结果分割成一个个文件名。 +四、通过os.path.dirname提取文件名中的目录部分,通过os.path.basename提取文件名称。 +五、通过os.path.join构建本地的目录结构。 +六、通过os.makedirs创建本地目录。 +七、通过scp命令将文件拷贝到本地。 + +这个代码的弊端:每次拷贝都需要输入密码,如果有很多文件需要拷贝,就需要输入很多次密码。 + +```python +import os +import subprocess + +# 本地的根目录 +local_root = "D:/" + +# 服务器的根目录 +remote_root = "/opt/radac/www/data" + +# 服务器用户名和服务器地址 +user = "radac" +server = "192.168.45.71" +password = "radac" + +# 日期列表 +dates = ["0520", "0521", "0522"] + +for date in dates: + # 寻找指定日期的文件 + cmd = f'sshpass -p {password} ssh {user}@{server} find {remote_root} -name "*{date}*" -type f -print' + result = subprocess.getoutput(cmd) + files = result.split('\n') + for file in files: + # 在这里对每个文件进行操作,比如打印文件名 + if "Permission denied" in file: + continue + + print(file) + + # 提取文件名中的目录部分 + dir = os.path.dirname(file) + # print(dir) + + # 提取目录中的最后一个部分作为名称 + name = os.path.basename(file) + # print(name) + + # 构建本地的目录结构 + new_path = dir.replace(remote_root, "") + # print(new_path) + + local_dir = os.path.join(local_root, new_path) + print(local_dir) + + # 如果本地目录不存在,则创建它 + os.makedirs(local_dir, exist_ok=True) + + # 下载文件到本地目录 + cmd = f'sshpass -p {password} scp {user}@{server}:{file} {local_dir}' + subprocess.run(cmd, shell=True) +``` + +## 公钥 + +来源:ChatGPT + +要设置 SSH 公钥身份验证,您需要在本地计算机上生成一对公钥和私钥。然后,将公钥复制到远程服务器上的 `~/.ssh/authorized_keys` 文件中。 + +以下是在 Linux上设置 SSH 公钥身份验证的步骤: + +1. 在本地计算机上生成一对公钥和私钥。打开终端并输入以下命令: + + **ssh-keygen -t rsa** + + 按照提示输入文件名和密码。如果您不想设置密码,请直接按 Enter 键。 + +2. 将公钥复制到远程服务器上的 `~/.ssh/authorized_keys` 文件中。打开终端并输入以下命令: + + **ssh-copy-id user@server** + + 其中 `user` 是您在远程服务器上的用户名,`server` 是远程服务器的 IP 地址或域名。如果您无法使用 `ssh-copy-id` 命令,请手动将公钥复制到远程服务器上的 `~/.ssh/authorized_keys` 文件中。 + +3. 确保远程服务器上的 `~/.ssh` 目录和 `authorized_keys` 文件的权限正确。打开终端并输入以下命令: + + **chmod 700 ~/.ssh** + + **chmod 600 ~/.ssh/authorized_keys** + + 这将确保只有您可以访问 `~/.ssh` 目录和 `authorized_keys` 文件。 + +请注意,如果您没有在远程服务器上写入文件的权限,则无法将公钥复制到 ~/.ssh/authorized_keys 文件中。在这种情况下,您需要联系服务器管理员,请求他们将公钥添加到 authorized_keys 文件中。 \ No newline at end of file diff --git "a/_posts/2023-05-23-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\344\270\200\357\274\211- \346\267\261\345\272\246\345\255\246\344\271\240\347\232\204\345\217\221\345\261\225\350\204\211\347\273\234.md" "b/_posts/2023-05-23-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\344\270\200\357\274\211- \346\267\261\345\272\246\345\255\246\344\271\240\347\232\204\345\217\221\345\261\225\350\204\211\347\273\234.md" new file mode 100644 index 0000000000..059f516b90 --- /dev/null +++ "b/_posts/2023-05-23-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\344\270\200\357\274\211- \346\267\261\345\272\246\345\255\246\344\271\240\347\232\204\345\217\221\345\261\225\350\204\211\347\273\234.md" @@ -0,0 +1,162 @@ +--- +layout: post +title: 深度学习与图像处理(一)- 深度学习的发展脉络 +date: 2023-05-23 +description: "深度学习与图像处理(一)- 深度学习的发展脉络" +tag: 机器学习 +--- +## 深度学习的发展脉络 + +### 神经元模型与感知机 + +每个神经元可以与多达成百上千个其他神经元的突触进行连接,接收从各个突触传来的信号输入。神经元细胞体的膜电位是它所有突触产生的电位总和,当膜电位超过某一阈值时,神经元会被激活并产生一个脉冲信号。脉冲信号将通过突触传递给下一个神经元。 + +$\mathrm{M}-\mathrm{P}$ 神经元模型正是对生物神经元模型的一个抽象和简化, 如图1所示为 **M-P 神经元模型**的示意图。其中, $x_1, x_2, \cdots, x_n$ 表示输入信号, $w_1, w_2, \cdots, w_n$ 表示权重, ○表示神经元, $y$表示输出信号。当输入信号被传送给神经元时, 会被分别乘以固定的权重。神经元会计算所有被传送过来的信号总和, 当总和超过阈值 $b$ 时, 神经元被激活, 此时输出1;否则,输出0。上述过程可以使用公式表达如下: + +$$ +y= \begin{cases}0, & x_1 w_1+x_2 w_2+\cdots+x_n w_n \leqslant b \\ 1, & x_1 w_1+x_2 w_2+\cdots+x_n w_n>b\end{cases} +$$ + +![20230523092229](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230523092229.png) + +图1 M-P 神经元模型示意图 + +可以看出, M-P 神经元模型的本质是进行**加权求和运算并根据阈值进行激活**的过程。 + +### 感知机 + +M-P 模型的权重和偏置是固定的,因此不具备学习的可能。1958 年由美国心理学家Rosenblatt 解提出了第一个具有学习能力的单层计算单元的神经网络,称之为感知机(perceptron)。 + +单层感知机的出现成为神经网络发展的基石。 + +如图2所示,输入x 的层可以叫作感知层,包含n个神经元,每个神经元接收一个输入信号,这n 个神经元仅将外部输入信息接收进来。用符号 X 表示这 n 个神经元的输入,则 $X = [x_1, x_2 ,\cdots, x_n]$。 + +处理层包含 m 个神经元,用符号W 表示,这m 个神经元可以处理感知层传输过来的信息,具有信息处理能力,并将处理后的信息进行输出。其中,符号W 表示进行数据处理时,感知层和处理层之间的连接权值,则$W = [w_1, w_2, \cdots, w_m ]$,符号 O 表示经处理层处理后输出的信息,则$O = [o_1,o_2,\cdots,o_m]$。 + +用感知机的处理过程这种方式改写一下之前的M-P 神经元模型。 + +$$ +o_{i}=\left\{\begin{array}{ll} +1, & b+W^{\mathrm{T}} X \leqslant 0 \\ +0, & b+W^{\mathrm{T}} X>0 +\end{array}\right. +$$ + +W 为权值矩阵,即权重,b 的含义为偏置。权重表示了输入的每个信号的重要程度,而偏置表示了神经元被激活的难易程度。感知机将输入的数据乘以权值之后,加上偏置,若得到的值小于等于0,则输出1,神经元被激活,否则输出0。可见,b 的值越大,则得到的输出值$o_i$ 为1 的可能性就越小,神经元越难被激活。 + +单层感知机能够将输入特征与权重和偏置进行**线性组合**,并利用线性预测函数实现对输入特征的分类,最终得到了0 或1 的输出,这在机器学习中属于二分类的学习算法,是一种线性分类器。我们将单层感知机的这种处理方式放在输入空间来理解,则感知机算法相当于找到了一个将实例划分为正样本和负样本的分离超平面。 + +单层感知机只能表示一条直线分割的空间,仅支持线性可分的数据集,而对非线性空间,如“异或”这种非线性运算,单层感知机不具备对其进行划分的能力。 + +![20230523174539](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230523174539.png) + +通过在输入层和输出层之间加入新的层,来实现弯曲折线的划分效果,从而产生了多层感知机(multilayer perceptron,MLP)。它除了输入层和输出层,中间可以有多个称作隐藏层的层。单层感知机无法表示的非线性空间,可以通过增加隐藏层来进行划分。 + +![20230523111721](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230523111721.png) + +### 从多层感知机到神经网络 + +多层感知机中的权重和偏置参数目前仍然是人为设定的,而为一个多层 +感知机确定出合适的、能够达到预期效果的权重和偏置参数是很难掌握的,而且也无法通过感知机的学习规则来进行权重训练。 + +基于反向传播算法,可以实现使网络在数据正向传播和误差反向传播两个过程中进行学习,从而从输入数据中自动地学习到合适的网络参数(权重和偏置),突破多层感知机的学习瓶颈,得到具备学习能力的神经网络。 + +神经网络在网络结构上可以看作是将多个感知机进行了组合,采用不同的方式来连接多个感知机,并采用不同的激活函数进行激活,同时神经网络具备参数的学习能力。 + +### 激活函数 + +为什么要使用激活函数呢? + +那是因为在神经网络中不使用激活函数的话,则相当于网络中每一层神经元的输入都是上一层神经元输出的线性组合,那么,很容易验证,无论在神经网络中设置了多少隐藏层,最终得到的输出都将是输入特征的线性组合,这样构建出的神经网络与没有隐藏层的单层感知机效果相当,神经网络的拟合能力也非常有限。 + +而引入非线性函数作为激活函数,就相当于为神经网络中的神经元引入了非线性因素,使得神经网络具备了学习的能力及更强大的拟合能力, +能够更好地拟合任何非线性函数的目标函数,从而将神经网络应用到更多的非线性模型中。激活函数是感知机与神经网络之间的纽带。 + +常见的激活函数包括: + +1. Sigmoid 函数(又称为 Logistic 函数): + +$$ +f(x) = \frac{1}{1+e^{-x}} +$$ + +![sigmod函数](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/sigmod函数.jpg) + +在输入值趋近于正无穷或者负无穷时,Sigmoid 函数的导函数值 +趋近于0,在反向传播时,Sigmoid 的这一特点会使得梯度更新十分缓慢,容易出现梯度消失的问题,从而无法完成深层网络的训练。Sigmoid 函数不是以0 为中心的函数,得到的输出值均为正,这会大大降低神经网络的收敛速度。 + +![20230523135539](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230523135539.png) + +2. Tanh函数: + +Tanh 函数与Sigmoid 函数相似,但Tanh 函数是原点对称的,其值域在−1~1之间:(解决了所有输出值符号相同的问题,能够对输入数据进行中心化,使得数据的均值更接近0,从而使下一层的学习变得简单一点。) + +$$ +f(x) = \frac{e^x-e^{-x}}{e^x+e^{-x}} = \frac{2}{1+e^{-2x}} -1 +$$ + +但是同样存在反向传播时候,梯度消失以及涉及幂运算,计算成本较高的问题。 + +![20230523133427](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230523133427.png) + +3. ReLU 函数: + +ReLU(rectified linear units)是目前设计神经网络使用非常广泛的激活函数,常用于**隐藏层神经元的输出**,表达式如下所示: + +$$ +f(x) = max(0,x) +$$ + +![20230523133531](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230523133531.png) + +当x≤0 时,ReLU 函数会将输入转换为0,而当x>0 时,ReLU函数的导数不变。 + +当使用ReLU 作为激活函数,输入为负值时,ReLU 函数会将输入转换为0,因 +而对应的神经元将不被激活,也就是说ReLU 作为激活函数进行激活时,在一段时 +间内,只有一部分神经元被激活,并不会同时激活所有的神经元,这一特性使得神经网络具有**稀疏性**,更便于计算。当输入为正值时,ReLU 函数能够保持梯度不衰减,不会出现梯度消失问题。另外,ReLU 函数的计算过程非常简单,使用ReLU作为激活函数只需要判断输入是否大于0,运算复杂度低,能够大大提高运算速度,网络的**收敛速度**也远快于Sigmoid 和Tanh。 + +4. Leaky ReLU 函数: + +Leaky ReLU 函数是对ReLU 函数的改进,其表达式如下所示: + +$$ +f(x) = max(0,x)+\alpha min(0,x) +$$ + +也即是, + +$$ +f(x) = \begin{cases} x, & \text{if $x>0$} \\ \alpha x, & \text{otherwise} \end{cases} +$$ + +该函数相比于ReLU,保留了一些负轴的值,缓解了激活值过小而导致神经元参数无法更新的问题,其中$\alpha$默认0.01。 + +![20230523134404](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230523134404.png) + +5. Softmax 函数: + +Softmax 可以将网络输出层中多个神经元得到的输出映射到0~1 之间,实现将 +网络的输出转换为每一类别的归一化概率,因此通常用于**多分类神经网络中分类器 +的输出层**,如目标检测中的分类器输出层。Softmax 函数的表达式如下所示: + +$$ +f(x_i) = \frac{e^{x_i}}{\sum_{j=1}^n e^{x_j}} +$$ + +其中,i表示第i个神经元,n表示输出层的神经元个数。 + +![20230523134948](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230523134948.png) + +这张图更加形象一点点: + +![20230523135259](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230523135259.png) + +### 如何选择不同的激活函数 + +应如何选择激活函数呢? + +通常Sigmoid、Tanh 和ReLU 都可以用在隐藏层中,但由于Sigmoid 和Tanh 存在梯度消失问题,会尽量避免使用。若要在隐藏层使用Sigmoid 或Tanh,由于Tanh 是以0 为中心的,通常性能会比Sigmoid函数要好。 + +ReLU 函数是一个比较通用的激活函数,在大多数的网络训练中都可以使用,但要注意ReLU 只能用于隐藏层中。 + +在分类器中,特别是二分类的情况,可以将Sigmoid 函数用于网络的输出层。在多分类问题中,可以将Softmax 函数用于网络的输出层。事实上,采用不同激活函数训练得到的网络性能好坏并没有一个统一的定论,一般来讲我们可以先采用ReLU 函数,若未能达到好的训练效果,再尝试采用其他激活函数。 diff --git "a/_posts/2023-05-23-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\344\272\214\357\274\211- \347\245\236\347\273\217\347\275\221\347\273\234\347\232\204\345\255\246\344\271\240.md" "b/_posts/2023-05-23-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\344\272\214\357\274\211- \347\245\236\347\273\217\347\275\221\347\273\234\347\232\204\345\255\246\344\271\240.md" new file mode 100644 index 0000000000..c028b43d7c --- /dev/null +++ "b/_posts/2023-05-23-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\344\272\214\357\274\211- \347\245\236\347\273\217\347\275\221\347\273\234\347\232\204\345\255\246\344\271\240.md" @@ -0,0 +1,192 @@ +--- +layout: post +title: 深度学习与图像处理(二)- 神经网络的学习 +date: 2023-05-23 +description: "深度学习与图像处理(二)- 神经网络的学习" +tag: 机器学习 +--- +## 神经网络的学习 + +神经网络的特征就是可以从数据中学习。所谓“从数据中学习”,是指可以由数据自动决定权重参数的值。 + +数据是机器学习的命根子。从数据中寻找答案、从数据中发现模式、根据数据讲故事…… + +### 训练集和测试集 + +机器学习中,一般将数据分为训练数据和测试数据两部分来进行学习和实验等。 + +首先,使用训练数据进行学习,寻找最优的参数;然后,使用测试数据评价训练得到的模型的实际能力。 + +为什么需要将数据分为训练数据和测试数据呢?因为我们追求的是模型的泛化能力。泛化能力是指处理未被观察过的数据(不包含在训练数据中的数据)的能力。获得泛化能力是机器学习的最终目标。 + +只对某个数据集过度拟合的状态称为过拟合(over fitting)。避免过拟合也是机器学习的一个重要课题。 + +### mini-batch学习 + +如果遇到大数据,这种情况下以全部数据为对象计算损失函数是不现实的。因此,我们从全部数据中选出一部分,作为全部数据的“近似”。神经网络的学习也是从训练数据中选出一批数据(称为mini-batch, 小批量),然后对每个mini-batch 进行学习。 + +mini-batch: 从训练数据中随机选出一部分数据,这部分数据称为mini-batch。 + +### 损失函数 + +神经网络以某个指标为线索寻找最优权重参数。神经网络的学习中所用的指标称为损失函数(loss function)。这个损失函数可以使用任意函数,但一般用均方误差和交叉熵误差等。 + +对该权重参数的损失函数求导,表示的是“如果稍微改变这个权重参数的值,损失函数的值会如何变化”。如果导数的值为负,通过使该权重参数向正方向改变,可以减小损失函数的值;反过来,如果导数的值为正,则通过使该权重参数向负方向改变,可以减小损失函数的值。不过,当导数的值为0 时,无论权重参数向哪个方向变化,损失函数的值都不会改变,此时该权重参数的更新会停在此处。 + +#### 均方误差 + +mean squared error,MSE: + +$$ +E = \frac{1}{2}\sum_{k}(y_k-t_k)^2 +$$ + +这里,yk 是表示神经网络的输出,tk 表示监督数据,k 表示数据的维数。 + +#### 交叉熵误差 + +cross entropy error,CEE: + +$$ +E = -\sum_{k}t_k\log y_k +$$ + +这里,yk是神经网络的输出,tk是正确解标签。并且,tk中只有正确解标签的索引为1,其他均为0(one-hot 表示)。 + +## 梯度法 + +梯度法是一种求函数最小值的方法。梯度表示的是各点处的函数值减小最多的方向。梯度法是沿着梯度方向前进一定距离,然后在新的地方重新求梯度,再沿着新梯度方向前进,如此反复,不断地沿梯度方向前进。这样一来,逐渐减小函数值的过程就是梯度法。 + +用梯度法寻找损失函数的最小值,寻找最优化参数(权重和偏置)。 + +用数学表达式来表示梯度法: + +$$ +x _ { 0 } = x _ { 0 } - \eta \frac { \partial f } { \partial x _ { 0 } } +$$ + +在这个式子中,$\eta$表示学习率(Learning rate),这个需要事先设定,比如说0.01或者0.001。 + +神经网络的学习包括,(1)选取训练集和测试集;(2)梯度计算;(3)更新参数;(4)重复以上操作。 + +## 误差反向传播法 + +通过数值微分计算神经网络的权重参数的梯度计算量大,因此,我们使用误差反向传播法(error backpropagation)来高效地计算梯度。 + +### 计算图 + +计算图可以将计算过程用图形来表示,通过多个节点和边表示。如图所示,下面的计算图表示 + +![20230524094303](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230524094303.png) + +构建计算图,然后从左到右计算的过程叫做正向传播,从右往左的计算过程叫做反向传播。通过将数据正向和反向传播,可以高效地计算权重参数的梯度。 + +## 与学习有关的技巧 + +### 权重的初始值 + +在神经网络的学习中,权重的初始值特别重要。实际上,设定什么样的 +权重初始值,经常关系到神经网络的学习能否成功。 + +一般会将初始值设为较小的值。比如说,权重初始值设置成0.01 * np.random.randn(10, 100)这样,使用由高斯分布生成的值乘以0.01 后得到的值(标准差为0.01 的高斯分布)。 + +### 权重的更新 + +#### SGD 随机梯度下降 + +随机梯度下降的思想就是随机采样一个样本来更新参数,SGD每次迭代仅对一个样本计算梯度,直到收敛。 + +SGD的缺点是,如果函数的形状非均向(anisotropic),比如呈延伸状,搜索 +的路径就会非常低效。因此,我们需要比单纯朝梯度方向前进的SGD更聪明的方法。SGD低效的根本原因是,梯度的方向并没有指向最小值的方向。 + +![20230525091300](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525091300.png) + +#### Momentum + +$$ +v \leftarrow \alpha v- \eta \frac{\partial L}{\partial W} + +\\ W+W+v \\ +$$ + +这里新出现了一个变量v,对应物理上的速度。表示了物体在梯度方向上受力,在这个力的作用下,物体的速度增加这一物理法则。 + +![20230525091822](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525091822.png) + +### AdaGrad + +AdaGrad会为参数的每个元素适当地调整学习率,与此同时进行学习。 + +$$ +h \leftarrow h+ \frac{\partial L}{\partial W} \odot \frac{\partial L}{\partial W} +$$ + +$$ +W \leftarrow W- \eta \frac{1}{\sqrt{h}} \frac{\partial L}{\partial W} +$$ + +这里,W表示要更新的权重,$\frac{\partial L}{\partial W}$表示损失函数关于W的梯度,$\eta$表示学习率,h表示一个变量,初期时所有元素都为0,它保存了以前的所有梯度的平方和,$\odot$表示矩阵的按元素乘法。 + +因此,AdaGrad会使得学习率逐渐减小(学习率衰减)。 + +![20230525092727](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525092727.png) + +### Batch Normalization + +Batch Norm的思路是调整各层的激活值分布使其拥有适当的广度。 + +Batch Norm有以下优点。 + +* 可以使学习快速进行(可以增大学习率)。 +* 不那么依赖初始值(对于初始值不用那么神经质)。 +* 抑制过拟合(降低Dropout等的必要性)。 + +### 正则化 + +#### 过拟合 + +发生过拟合主要有以下两个原因: + +* 模型拥有大量参数、表现力强 +* 训练数据少 + +#### 权重衰减 + +权值衰减是一直以来经常被使用的一种抑制过拟合的方法。该方法通过 +在学习的过程中对大的权重进行惩罚,来抑制过拟合。 + +Dropout 是一种在学习的过程中随机删除神经元的方法。训练时,随机 +选出隐藏层的神经元,然后将其删除。被删除的神经元不再进行信号的传递。 + +### 超参数的验证 + +除了权重和偏置等参数,超参数(hyper-parameter)也经常出现。这里所说的超参数是指,比如各层的神经元数量、batch 大小、参数更新时的学习率或权值衰减等。如果这些超参数没有设置合适的值,模型的性能就会很差。 + +**不能使用测试数据评估超参数的性能。** + +为什么不能用测试数据评估超参数的性能呢?这是因为如果使用测试数 +据调整超参数,超参数的值会对测试数据发生过拟合。可能就会得到不能拟合其他数据、泛化能力低的模型。 + +因此,调整超参数时,必须使用超参数专用的确认数据。用于调整超参数的数据,一般称为验证数据(validation data)。我们使用这个验证数据来评估超参数的好坏。 + +```python +(x_train,t_train),(x_test,t_test)=load_mnist() + +# shuttle the data +x_train,t_train = shuffle_data(x_train,t_train) + +# seperate validation data + +validation_rate = 0.20 +validation_num = int(x_train.shape[0]*validation_rate) + +x_val = x_train[:validation_num] +t_val = t_train[:validation_num] + +x_train = x_train[validation_num:] +t_train = t_train[validation_num:] +``` + +### 集成学习 + +让多个模型进行学习,推理时再取多个模型的输出的平均值。 diff --git "a/_posts/2023-05-24-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\344\270\211\357\274\211- \345\215\267\347\247\257\347\245\236\347\273\217\347\275\221\347\273\234.md" "b/_posts/2023-05-24-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\344\270\211\357\274\211- \345\215\267\347\247\257\347\245\236\347\273\217\347\275\221\347\273\234.md" new file mode 100644 index 0000000000..eb21c395fc --- /dev/null +++ "b/_posts/2023-05-24-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\344\270\211\357\274\211- \345\215\267\347\247\257\347\245\236\347\273\217\347\275\221\347\273\234.md" @@ -0,0 +1,103 @@ +--- +layout: post +title: 深度学习与图像处理(三)- 卷积神经网络 +date: 2023-05-24 +description: "深度学习与图像处理(三)- 卷积神经网络" +tag: 机器学习 +--- +## 卷积神经网络 + +Convolutional Neural Network卷积神经网络被广泛用于图像的识别,语音的处理等等,与之前的神经网络相比,出现了卷积层和池化层。 + +### 卷积层 + +CNN中,有时将卷积层的输入输出数据称为特征图(feature map)。其中,卷积层的输入数据称为输入特征图(input feature map),输出数据称为输出特征图(output feature map)。 + +![20230525100908](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525100908.png) + +如图所示,卷积运算相当于对输入数据应用滤波器,输入大小是(4,4),输出大小是(2,2),滤波器大小是(3,3)。对于输入数据,卷积运算以一定间隔滑动滤波器的窗口并应用。如下图所示,将各个位置上滤波器的元素和输入的对应元素相乘,然后再求和(有时将这个计算称为乘积累加运算)。然后,将这个结果保存到输出的对应位置。 + +![20230525101131](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525101131.png) + +#### 填充 + +在进行卷积层的处理之前,有时要向输入数据的周围填入固定的数据(比 +如0 等),这称为填充(padding)。 + +使用填充主要是为了调整输出的大小。比如,对大小为(4, 4) 的输入数据应用(3, 3) 的滤波器时,输出大小变为(2, 2),相当于输出大小比输入大小缩小了2 个元素。这在反复进行多次卷积运算的深度网络中会成为问题。 + +为了避免出现这样的情况,就要使用填充。在刚才的例子中,将填充的幅度设为1,那么相对于输入大小(4, 4),输出大小也保持为原来的(4, 4),卷积运算可以在保持空间大小不变的情况下将数据传递给下一层。 + +![20230525102624](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525102624.png) + +#### 步幅 + +应用滤波器的位置间隔称为步幅(stride)。之前的例子中步幅都是1。以下是步幅为2的卷积运算的例子: + +![20230525102931](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525102931.png) + +#### 卷积前后的大小 + +假设输入前的大小为(H,W),滤波器大小为(FH, FW),输出大小为(OH,OW),填充为P,步幅为S。此时,输出大小可通过下面的算式进行计算。 + +$$ +\begin{matrix}OH= \frac{H+2P-FH}{S}+1 \\ OW= \frac{W+2P-FW}{S}+1 \\ \end{matrix} +$$ + +图像除了长高方向之外,还需要处理通道方向。一般来说,先进行卷积计算,然后再把三个通道的计算结果相加,从而得到输出。 + +![20230525103528](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525103528.png) + +如果要在通道方向上也拥有多个卷积运算的输出,比如说有FN个滤波器,通过应用FN个滤波器,输出特征图也有FN个。作为4 维数据,滤波器的权重数据要按(output_channel, input_channel, height, width) 的顺序书写。比如,通道数为3、大小为5 × 5 的滤波器有20 个时,可以写成(20, 3, 5, 5)。 + +![20230525103737](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525103737.png) + +我们希望卷积运算也同样对应批处理,比如说如下图所示,对N个数据进行批处理时,数据的输入输出形状如图所示。 + +![20230525104123](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525104123.png) + +### 池化层 + +池化是缩小高、长方向上的空间的运算。比如,如下图所示,进行将2 × 2 的区域集约成1 个元素的处理,取这个2 × 2 区域的最大值被称为Max池化,可以缩小空间大小。 + +![20230525104314](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525104314.png) + +在这个例子中,步幅设置为2,所以2 × 2窗口的移动间隔为2个元素。 + +池化计算不会改变输入数据的通道数,是按照通道独立进行的。 + +![20230525104544](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525104544.png) + +### 全连接层 + +比如说输入图像是一通道,高28像素,宽28像素,经过全连接层之后被排列成一列,以784个数据的形式。 + +### CNN在看什么? + +局部特征,如边缘或斑块等原始信息,则随着层次加深,提取的信息也愈加复杂、抽象。 + +#### CNN的实现 + +组合卷积层和池化层,可以构建出CNN。CNN的结构如下图所示。 + +![20230525105727](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525105727.png) + +## 经典CNN + +### LeNet + +它有连续的卷积层和池化层(正确地讲,是只“抽选元素”的子采样层),最 +后经全连接层输出结果。 + +![20230525110448](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525110448.png) + +和“现在的CNN”相比,LeNet 有几个不同点。第一个不同点在于激活函数。LeNet 中使用sigmoid 函数,而现在的CNN中主要使用ReLU函数。 + +此外,原始的LeNet 中使用子采样(subsampling)缩小中间数据的大小,而 +现在的CNN中Max池化是主流。 + +### AlexNet + +它的网络结构和LeNet基本上没有什么不同,但是激活函数使用ReLU;使用进行局部正规化的LRN(Local Response Normalization)层;使用Dropout。 + +![20230525110632](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525110632.png) diff --git "a/_posts/2023-05-24-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\345\233\233\357\274\211- \346\267\261\345\261\202\347\245\236\347\273\217\347\275\221\347\273\234.md" "b/_posts/2023-05-24-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\345\233\233\357\274\211- \346\267\261\345\261\202\347\245\236\347\273\217\347\275\221\347\273\234.md" new file mode 100644 index 0000000000..0b9df0eede --- /dev/null +++ "b/_posts/2023-05-24-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\345\233\233\357\274\211- \346\267\261\345\261\202\347\245\236\347\273\217\347\275\221\347\273\234.md" @@ -0,0 +1,60 @@ +--- +layout: post +title: 深度学习与图像处理(四)- 深层神经网络 +date: 2023-05-24 +description: "深度学习与图像处理(四)" +tag: 机器学习 +--- +## 深层神经网络 + +与浅层神经网络相比,深层神经网络的层数更多,参数更多,计算量更大,但是可以更好地拟合数据,提高模型的准确率。 + +![20230525110901](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525110901.png) + +我们思考一个问题,每次输出数据的各个节点是从输入数据的哪个区域计算得到的呢?下图是重复两次3×3的卷积计算的情形,可以看到,输出数据的各个节点都来自于输入数据的一个5×5的区域,这个区域称为感受野。 + +![20230525111418](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525111418.png) + +也就是说,一次5 × 5 的卷积运算的区域可以由两次3 × 3 的卷积运算抵充。并且,相对于前者的参数数量25(5 × 5),后者一共是18(2 × 3 × 3),通过叠加卷积层,参数数量减少了。同时,可以扩大感受野(receptive field)。通过叠加层,在卷积层中间加入ReLU层,可以叠加非线性,提高模型的表达能力。 + +## 经典的深度网络 + +### VGG + +VGG是由卷积层和池化层构成的基础的CNN,它的特点在于将有权重的层(卷积层或者全连接层)叠加至16 层(或者19 层),具备了深度(根据层的深度,有时也称为“VGG16”或“VGG19”)。 + +VGG中需要注意的地方是,基于3×3 的小型滤波器的卷积层的运算是连续进行的。如图所示,重复进行“卷积层重叠2 次到4 次,再通过池化层将大小减半”的处理,最后经由全连接层输出结果。 + +![20230525112035](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525112035.png) + +### GoogLeNet + +GoogLeNet的特征是,网络不仅在纵向上有深度,在横向上也有深度(广度),这称为“Inception 结构”。 + +如图所示,Inception 结构使用了多个大小不同的滤波器(和池化),最后再合并它们的结果。GoogLeNet 的特征就是将这个Inception 结构用作一个构件(构成元素)。此外,Inception中大量采用了1×1的卷积运算,这是因为1×1的卷积运算可以用来控制通道数,从而控制数据的维度。 + +![20230525113350](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525113350.png) + +### ResNet + +![20230525113810](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230525113810.png) + +快捷结构将数据原封不动地传向下游。因此,基于快捷结构,不用担心梯度会变小(或变大),之前因为加深层而导致的梯度变小的梯度消失问题就有望得到缓解。 + +### 迁移学习 + +实践中经常会灵活应用使用ImageNet 这个巨大的数据集学习到的权重数据,这称为迁移学习,将学习完的权重(的一部分)复制到其他神经网络,进行再学习(fine tuning)。比如,准备一个和VGG 相同结构的网络,把学习完的权重作为初始值,以新数据集为对象,进行再学习。迁移学习在手头数据集较少时非常有效。 + +## 常见的图像处理数据集 + +### ImageNet + +### CIFAR-10 + +### Microsoft COCO + +### PASCAL VOC + + diff --git "a/_posts/2023-05-25-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\344\272\224\357\274\211- \345\233\276\345\203\217\345\244\204\347\220\206.md" "b/_posts/2023-05-25-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\344\272\224\357\274\211- \345\233\276\345\203\217\345\244\204\347\220\206.md" new file mode 100644 index 0000000000..4b9eee60c3 --- /dev/null +++ "b/_posts/2023-05-25-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\344\272\224\357\274\211- \345\233\276\345\203\217\345\244\204\347\220\206.md" @@ -0,0 +1,149 @@ +--- +layout: post +title: 深度学习与图像处理(五)- 图像处理 +date: 2023-05-25 +description: "深度学习与图像处理(五)- 图像处理" +tag: 机器学习 +--- +## 基本的图像操作和处理 + +### PIL库 + +PIL库(python imaging library)是Python中最常用的图像处理库,它提供了很多有用的函数和类,可以帮助我们快速完成图像处理的工作。 + +```python +from PIL import Image +pil_image = Image.open('test.jpg') # 打开图像,创建一个Image对象 +pil_image.show() # 显示图像 +pil_image.save('test.png') # 保存图像 +pil_image.size # 图像的尺寸 +pil_image_convert = pil_image.convert('L') # 转换图像为灰度图像 +``` + +读取文件夹下所有文件并且转换文件格式为jpg + +```python +# 文件夹中所有图像文件的列表 +from PIL import Image +import os + +for infile in filelist: + outfile = os.path.splitext(infile)[0] + ".jpg" + if infile != outfile: + try: + Image.open(infile).save(outfile) + except IOError: + print("cannot convert", infile) +``` + +os.path.splitext(infile)[0]表示文件名,os.path.splitext(infile)[1]表示文件扩展名,filelist是文件列表。 + +outfile = os.path.splitext(infile)[0] + ".jpg"表示将文件扩展名改为jpg。 + +如何获取文件列表 + +```python +import os +filelist = os.listdir('test') # test是文件夹名 +``` + +其他操作: + +```python +pil_image.thumbnail((128,128)) # 缩略图 +pil_image.rotate(45) # 旋转图像 +pil_image.transpose(Image.FLIP_LEFT_RIGHT) # 左右翻转 +pil_image.transpose(Image.FLIP_TOP_BOTTOM) # 上下翻转 +# 裁剪图像 +box = (100,100,400,400) # 左上角和右下角坐标 +region = pil_image.crop(box) # 裁剪图像 + +# 调整图像大小 +out = pil_image.resize((128,128)) # 128*128 +``` + +### OpenCV + +OpenCV可以用于实时处理计算机视觉问题。 + +OpenCV 2.3.1 版本实际上提供了两个Python 接口。旧的cv 模块使用OpenCV 内部数据类型,并且从NumPy 使用起来可能需要一些技巧。新的cv2 模块用到了NumPy 数组,并且使用起来更加直观。 + +```python +import cv2 +im = cv2.imread('test.jpg') # 读取图像 +h,w = im.shape[:2] # 获取图像的高和宽 :2表示前两个元素 +cv2.imshow('image',im) # 显示图像 +cv2.imwrite('test_output.png',im) # 保存图像 +``` + +在openCV中,颜色空间不是RGB,而是BGR,所以在显示图像时,需要将BGR转换为RGB。 + +```python +import cv2 +im = cv2.imread('test.jpg') +b,g,r = cv2.split(im) # 分离图像的通道 +im = cv2.merge((r,g,b)) # 合并图像的通道 +``` + +```python +im = cv2.imread('empire.jpg') +# 创建灰度图像 +gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) +# BGR2RGB +im = cv2.cvtColor(im,cv2.COLOR_BGR2RGB) +``` + +#### 处理视频 + +OpenCV可以用于处理视频,可以从摄像头中读取视频,也可以从文件中读取视频。 + +```python +import cv2 + +cap = cv2.VideoCapture(0) # 从摄像头中读取视频 +# cap = cv2.VideoCapture('test.avi') # 从文件中读取视频 +while True: + ret,frame = cap.read() # 读取一帧 + cv2.imshow('frame',frame) # 显示一帧 + key = cv2.waitKey(10) + if key == 27: + break + if key == ord(' '): + cv2.imwrite('vid_result.jpg',frame) + +cap.release() # 释放摄像头 +cv2.destroyAllWindows() # 关闭窗口 +``` + +while True:表示循环读取视频帧,直到按下ESC键退出程序。 +cat.read()返回两个值,第一个值ret表示是否读取成功,第二个值frame表示读取的帧。 + +key = cv2.waitKey(10)表示等待10ms,如果在10ms内按下了键盘,则返回按键的ASCII码,否则返回-1。 + +key = ord(' ')表示按下空格键,将当前帧保存为vid_result.jpg。 + +key = 27表示按下ESC键,退出程序。 + +##### 将视频读取到NumPy数组中 + +```python +import cv2 +import numpy as np + +cap = cv2.VideoCapture(0) # 从摄像头中读取视频 + +frames = [] + +while True: + ret,frame = cap.read() # 读取一帧 + cv2.imshow('frame',frame) # 显示一帧 + frames.append(frame) + if cv2.waitKey(10) == 27: + break + frame = array(frames) + +print(frame.shape) # (480, 640, 3) +print(frames.shape) # (480, 640, 3, 100) +``` + +最终得到的数组会有帧数、帧高、帧宽及颜色通道数(3 个). diff --git "a/_posts/2023-05-26-Ubuntu\345\256\211\350\243\205Docker.md" "b/_posts/2023-05-26-Ubuntu\345\256\211\350\243\205Docker.md" new file mode 100644 index 0000000000..6bdfefea68 --- /dev/null +++ "b/_posts/2023-05-26-Ubuntu\345\256\211\350\243\205Docker.md" @@ -0,0 +1,113 @@ +--- +layout: post +title: Ubuntu安装Docker +date: 2023-05-26 +description: "Ubuntu安装Docker" +tag: Linux +--- + + +## 在ubuntu中安装 + +在linux系统中安装Docker非常简单,官方为我们提供了一键安装脚本。这个方法也适用于Debian或CentOS等发行版。 + +``` +curl -sSL https://get.daocloud.io/docker | sh +``` + +在线安装其他方法: + +``` +curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun +``` + +-fsSL:-f/--fail:连接失败时不显示http错误信息;-s/--silent:静默模式,不显示进度条或错误信息;-S/--show-error:显示错误信息;-L/--location:跟随重定向。 + +启动Docker后台服务: + +``` +systemctl enable docker +#启动Docker服务: +systemctl start docker +``` + +安装完成后,我们可以查看Docker版本信息: + +``` +docker version +``` + +## 安装docker-compose + +1. 登入[官方地址](https://github.com/docker/compose/releases)下载指定版本,官方地址为: +https://github.com/docker/compose/releases +下载docker-compose-linux-x86_64 + +2. 安装并且验证 + +```shell +mv docker-compose-Linux-x86_64 /usr/local/bin/docker-compose +chmod 777 /usr/local/bin/docker-compose +docker-compose --version +``` + + +## nvm安装对应版本的node + +1. 安装nvm + + + +```shell +wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash + +nvm --version +``` + +或者从gitee上下载 + +```shell +git clone https://gitee.com/mirrors/nvm +cd nvm +bash install.sh +``` + + +安装好之后显示如下信息: + +``` +=> Compressing and cleaning up git repository + +=> Appending nvm source string to /home/kan/.bashrc +=> Appending bash_completion source string to /home/kan/.bashrc +=> Close and reopen your terminal to start using nvm or run the following to use it now: + +export NVM_DIR="$HOME/.nvm" +[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm +[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion +``` + +2. node v12.18.1+npm 6.14.5安装 + +```shell +nvm install 12.18.1 +nvm use 12.18.1 +node -v +``` + +查看对应的npm版本 + +```shell +npm -v +``` + + +3. 卸载nvm + +```shell +cd ~ +rm -rf .nvm +``` + + + diff --git "a/_posts/2023-05-26-Ubuntu\351\235\231\346\200\201IP\350\256\276\347\275\256.md" "b/_posts/2023-05-26-Ubuntu\351\235\231\346\200\201IP\350\256\276\347\275\256.md" new file mode 100644 index 0000000000..c5eaf4bfdc --- /dev/null +++ "b/_posts/2023-05-26-Ubuntu\351\235\231\346\200\201IP\350\256\276\347\275\256.md" @@ -0,0 +1,104 @@ +--- +layout: post +title: Ubuntu静态IP设置 +date: 2023-05-26 +description: "Ubuntu静态IP设置" +tag: Linux +--- +## 网络计划 + +Ubuntu 17.10和更高版本使用Netplan作为默认网络管理工具。以前的Ubuntu版本正在使用ifconfig及其配置文件/etc/network/interfaces来配置网络。 + +设置静态IP地址的第一步是识别要配置的以太网接口的名称。为此,请使用ip link命令,如下所示: + +```shell +ip link +# or +ip addr +# or +ifconfig +``` + +在我的情况下,以太网接口的名称是wlx90de80a1d948。 + +## 查看gateway + +```shell +ip route +``` + +ip route命令显示路由表。在我的情况下,网关是 + +```txt +default via 172.17.16.1 dev wlx90de80a1d948 proto dhcp metric 600 +169.254.0.0/16 dev wlx90de80a1d948 scope link metric 1000 +172.17.16.0/21 dev wlx90de80a1d948 proto kernel scope link src 172.17.23.19 metric 600 +172.18.0.0/16 dev docker0 proto kernel scope link src 172.18.0.1 linkdown +``` + +## 配置静态IP地址 + +```shell +cd /etc/netplan +vim 00-installer-config.yaml +``` + +```txt + +# Let NetworkManager manage all devices on this system + +network: + + version: 2 + + renderer: NetworkManager + + ethernets: + + wlx90de80a1d948: + + dhcp4: yes + + addresses: + + - 172.17.23.19/24 + + gateway4: 172.17.16.1 + + nameservers: + + addresses: [8.8.8.8, 1.1.1.1] + +``` + +根据实际需要依次设置wlx90de80a1d948网卡的静态IP地址、网关、DNS,dhcp4设置为false。 + +使得更改生效: + +```shell +netplan apply +``` + +有时候根据方法一操作,仍然不能成功设置静态IP,原因一般是系统里面安装过ifupdown命令。这个命令会根据/etc/network/interfaces这个文件里的配置信息来设置网卡,会覆盖掉上面方法一中的设置,导致不起作用。这时候我们只需修改/etc/network/interfaces这个文件即可。 + +## 查看更改之后是否变成静态IP地址 + + + +```shell +ip addr show + +``` + +如果网络接口已配置为静态IP地址,则在输出中会看到以下内容: + +```shell +inet <静态IP地址>/<子网掩码> brd <广播地址> scope global <网络接口名称> +1 +``` + +如果网络接口已配置为动态IP地址,则在输出中会看到以下内容: + +```shell +inet <动态IP地址>/<子网掩码> brd <广播地址> scope global dynamic <网络接口名称> +``` diff --git "a/_posts/2023-05-26-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\345\205\255\357\274\211-\345\272\217\345\210\227\346\250\241\345\236\213.md" "b/_posts/2023-05-26-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\345\205\255\357\274\211-\345\272\217\345\210\227\346\250\241\345\236\213.md" new file mode 100644 index 0000000000..db747d157e --- /dev/null +++ "b/_posts/2023-05-26-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\345\205\255\357\274\211-\345\272\217\345\210\227\346\250\241\345\236\213.md" @@ -0,0 +1,16 @@ +--- +layout: post +title: 深度学习与图像处理(六)-序列模型 +date: 2023-05-26 +description: "深度学习与图像处理(六)-序列模型" +tag: 机器学习 +--- +## 序列模型(Sequence Models) + +## RNN + +## 门控循环单元 + +## 长短期记忆(LSTM(long short term memory)unit) + +## 双向RNN diff --git "a/_posts/2023-05-27-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\344\270\203\357\274\211- Pytorch\345\237\272\347\241\200\345\222\214\346\211\213\345\206\231\346\225\260\345\255\227\350\257\206\345\210\253\345\256\236\346\210\230.md" "b/_posts/2023-05-27-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\344\270\203\357\274\211- Pytorch\345\237\272\347\241\200\345\222\214\346\211\213\345\206\231\346\225\260\345\255\227\350\257\206\345\210\253\345\256\236\346\210\230.md" new file mode 100644 index 0000000000..fd4db5efc9 --- /dev/null +++ "b/_posts/2023-05-27-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\344\270\203\357\274\211- Pytorch\345\237\272\347\241\200\345\222\214\346\211\213\345\206\231\346\225\260\345\255\227\350\257\206\345\210\253\345\256\236\346\210\230.md" @@ -0,0 +1,794 @@ +--- +layout: post +title: 深度学习与图像处理(七)- Pytorch基础和手写数字识别实战 +date: 2023-05-27 +description: "深度学习与图像处理(七)" +tag: 机器学习 +--- +## 机器学习工作流 + +(1) 定义问题:有哪些数据可用?你想要预测什么?你是否需要收集更多数据或雇人为数据集手动添加标签? +(2) 找到能够可靠评估目标成功的方法。对于简单任务,可以用预测精度,但很多情况都需要与领域相关的复杂指标。 +(3) 准备用于评估模型的验证过程。训练集、验证集和测试集是必须定义的。验证集和测试集的标签不应该泄漏到训练数据中。举个例子,对于时序预测,验证数据和测试数据的时间都应该在训练数据之后。 +(4) 数据向量化。方法是将数据转换为向量并预处理,使其更容易被神经网络所处理(数据标准化等)。 +(5) 开发第一个模型,它要打败基于常识的简单基准方法,从而表明机器学习能够解决你的问题。事实上并非总是如此! +(6) 通过调节超参数和添加正则化来逐步改进模型架构。仅根据模型在验证集(不是测试集或训练集)上的性能来进行更改。请记住,你应该先让模型过拟合(从而找到比你的需求更大的模型容量),然后才开始添加正则化或降低模型尺寸。 +(7) 调节超参数时要小心验证集过拟合,即超参数可能会过于针对验证集而优化。我们保留一个单独的测试集,正是为了避免这个问题。 + +## Pytorch基础 + +### GPU和CPU + +GPU(Graphics Processing Unit)又被称作图像处理器,是一台主机的显示处理核心,主要负责对计算机中的图形和图像的处理与运算。 +它们之间的具体差异如下。 +(1)核心数:GPU 相较于 CPU 的硬件构成,在结构上拥有更多的核心数量,虽然 GPU的核心数量更多,但是 CPU 中的单个核心相对于 GPU 中的单个核心,拥有更快速、高效的算力。 +(2)应用场景:在应用场景上 GPU 和 CPU 也各有侧重,GPU 适用于需要并行计算能力的场景,比如图像处理;CPU 更适用于需要串行计算能力的场景,比如计算机的指令处理。 + +CPU 都是为了计算而生的,那么我们为什么还会强力推荐将 GPU作为核心算力的输出硬件呢?这是因为在深度学习模型中生成的参数结构都是张量(Tensor)形式的,矩阵和张量的算术运算的模式就是一种并行运算。所以张量的算术运算在 GPU 的加持 +下会获得比 CPU 更快速、高效的计算能力,因此在对深度学习参数的训练和优化过程中,GPU 能够为我们提供更多的帮助。 + +```python +import time +import torch +for i in range(1,10): + start = time.time() + a = torch.FloatTensor(i*100,1000,1000) + a = torch.matmul(a,a) + end = time.time()-start + print(end) +``` + +torch.FloatTensor()函数用于生成张量,torch.matmul()函数用于矩阵乘法运算,time.time()函数用于获取当前时间戳,end-start用于计算时间差。 + +在这里,张量的维度从(100,1000,1000)到(1000,1000,1000)逐次累加,所需要的运算耗时如下: + +```text +1.6489999294281006 +2.9369986057281494 +4.34499979019165 +5.8696980476379395 +7.847997665405273 +8.423005104064941 +9.7409987449646 +12.301092147827148 +14.527003526687622 +``` + +以上代码加上GPU加速(`a=a.cuda()`)后的运行时间几乎不会有变化,说明GPU 的并行计算能力很强。 + +检测是否支持GPU: + +```python +import torch +print(torch.cuda.is_available()) +print(torch.__version__) +``` + +### 张量Tensor + +张量是一个多维数组,它是一个标量、向量、矩阵的泛化。PyTorch中的张量和numpy中的array很类似。 + +| 函数名 | 数据类型 | +| ----------------------------- | ------------------------------------------- | +| torch.FloatTensor | 浮点型Tensor | +| torch.IntTensor | 整型Tensor | +| torch.rand | 生成在0-1之间均匀分布的指定维度的随机Tensor | +| torch.randn | 随机生成均值为0,方差为1的正太分布 | +| torch.range(start,range,step) | | +| torch.zeros | | + +#### tensor中的运算 + +torch.clamp(input, min, max, out=None) + +将输入input张量每个元素的夹紧到区间 [min,max],并返回结果到一个新张量。(元素小于裁剪范围时,取min;大于裁剪范围时,取max) + +### 自动梯度 + +实现自动梯度功能的过程大致为:先通过输入的 Tensor 数据类型的变量在神经网络的前向传播过程中生成一张计算图,然后根据这个计算图和输出结果准确计算出每个参数需要更新的梯度,最后通过完成后向传播完成对参数的梯度更新。 + +在实践中完成自动梯度需要用到 torch.autograd 包中的 Variable 类对我们定义的 Tensor +数据类型变量进行封装,在封装后,计算图中的各个节点就是一个 Variable 对象,这样才能应用自动梯度的功能。 + +用X代表我们选中的节点,X.grad是一个Variable对象,不过它表示的是X的梯度,在访问梯度时候要使用X.grad.data。 + +```python +# import time +# import torch +# for i in range(1,10): +# start = time.time() +# a = torch.FloatTensor(i*100,1000,1000) +# a = torch.matmul(a,a) +# end = time.time()-start +# print(end) + + +import torch +from torch.autograd import Variable +batch_n = 100 +hidden_layer = 100 +input_data = 1000 +output_data = 10 + +x = Variable(torch.randn(batch_n,input_data),requires_grad=False) +y = Variable(torch.randn(batch_n,output_data),requires_grad=False) + +w1 = Variable(torch.randn(input_data,hidden_layer),requires_grad=True) +w2 = Variable(torch.randn(hidden_layer,output_data),requires_grad=True) + +# 训练次数和学习率 +epoch_n = 20 +learning_rate = 1e-6 + +# 模型训练和参数更新 +for epoch in range(epoch_n): + y_pred = x.mm(w1).clamp(min=0).mm(w2) + # mm表示矩阵乘法, + #clamp表示夹紧,相当于激活函数 + loss = (y_pred - y).pow(2).sum() + print("Epoch:{},Loss:{:.4f}".format(epoch,loss.item())) + # loss.data是一个标量张量,它没有维度,因此不能使用loss.data[0]来访问它的值。 + + loss.backward() + + # 更新参数 + w1.data -= learning_rate * w1.grad.data + w2.data -= learning_rate * w2.grad.data + + w1.grad.data.zero_() # 梯度清零 + w2.grad.data.zero_() # 梯度清零 + +``` + +这段代码中,使用Variable类对Tensor数据类型进行了封装,requires_grad参数为False时候,变量在自动梯度计算的过程中不会保留梯度值。对于输入的x和输出的y,不需要保留梯度值;但是对于两个权重w1和w2,需要设置为True保留参数。 + +在代码的最后还要将本次计算得到的各个参数节点的梯度值通过 grad.data.zero_()全部置零,如果不置零,则计算的梯度值会被一直累加,这样就会影响到后续的计算。 + +输出结果如下: + +```text +Epoch:0,Loss:42458792.0000 +Epoch:1,Loss:80534832.0000 +Epoch:2,Loss:333504672.0000 +Epoch:3,Loss:851591680.0000 +Epoch:4,Loss:112547912.0000 +Epoch:5,Loss:26212034.0000 +Epoch:6,Loss:14902239.0000 +Epoch:7,Loss:9971557.0000 +Epoch:8,Loss:7236613.0000 +Epoch:9,Loss:5534734.0000 +Epoch:10,Loss:4387320.0000 +Epoch:11,Loss:3568915.2500 +Epoch:12,Loss:2962310.7500 +Epoch:13,Loss:2497898.7500 +Epoch:14,Loss:2133571.2500 +Epoch:15,Loss:1841966.7500 +Epoch:16,Loss:1605802.0000 +Epoch:17,Loss:1411681.5000 +Epoch:18,Loss:1250214.2500 +Epoch:19,Loss:1114617.1250 +``` + +除了采用自动梯度的方法,我们还可以通过构建一个继承了torch.mm.Module类的类来实现神经网络的前向传播和后向传播,这种方法更加灵活,可以自定义神经网络的结构。 + +下面介绍如何使用自定义传播函数的方法,来调整之前具备自动梯度功能的简易神经网络模型。 + +```python +import torch +from torch.autograd import Variable +batch_n = 100 +hidden_layer = 100 +input_data = 1000 +output_data = 10 + +x = Variable(torch.randn(batch_n,input_data),requires_grad=False) +y = Variable(torch.randn(batch_n,output_data),requires_grad=False) + +w1 = Variable(torch.randn(input_data,hidden_layer),requires_grad=True) +w2 = Variable(torch.randn(hidden_layer,output_data),requires_grad=True) + +# 训练次数和学习率 +epoch_n = 20 +learning_rate = 1e-6 + +class Model(torch.nn.Module): + def __init__(self): + super(Model,self).__init__() + def forward(self,input,w1,w2): + x = torch.mm(input,w1) + x = torch.clamp(x,min=0) + x = torch.mm(x,w2) + return x + def backward(self): + pass + +# 模型训练和参数更新 +model = Model() + +for epoch in range(epoch_n): + y_pred = model(x,w1,w2) + loss = (y_pred - y).pow(2).sum() + print("Epoch:{},Loss:{:.4f}".format(epoch,loss.item())) + + # 更新参数 + loss.backward() + w1.data -= learning_rate * w1.grad.data + w2.data -= learning_rate * w2.grad.data + + w1.grad.data.zero_() # 梯度清零 + w2.grad.data.zero_() # 梯度清零 +``` + +以上代码展示了一个比较常用的 Python 类的构造方式:首先通过 class Model(torch.nn. Module)完成了类继承的操作,之后分别是类的初始化,以及 forward 函数和 backward 函数。forward 函数实现了模型的前向传播中的矩阵运算,backward 实现了模型的后向传播中的自动梯度计算,后向传播如果没有特别的需求,则在一般情况下不用进行调整。在定义好类之后,我们就可以对其进行调用了(`model = Model()`),代码如下 + +### 模型搭建和优化 + +PyTorch 中已定义的类和方法覆盖了神经网络中的线性变换、激活函数、卷积层、全连接层、池化层等常用神 +经网络结构的实现。 + +#### torcn.nn + +下面是使用Pytorch中的torch.nn简化之前的代码,这里仅仅定义了4个变量,分别是输入层的神经元个数、隐藏层的神经元个数、输出层的神经元个数和训练次数。但是这里仅仅定义了输入和输出的变量,之前代码中的权重w1和w2都没有定义,这是因为在torch.nn中,我们可以自动生成和初始化对应维度的权重参数。 + +```python +import torch +from torch.autograd import Variable +batch_n = 100 +hidden_layer = 100 +input_data = 1000 +output_data = 10 + +x = Variable(torch.randn(batch_n,input_data),requires_grad=False) +y = Variable(torch.randn(batch_n,output_data),requires_grad=False) + +# 使用torch.nn搭建模型 + +models = torch.nn.Sequential( + torch.nn.Linear(input_data,hidden_layer), + torch.nn.ReLU(), + torch.nn.Linear(hidden_layer,output_data) +) +``` + +torch.nn.Sequential 括号内的内容就是我们搭建的神经网络模型的具体结构,这里首先通过 torch.nn.Linear(input_data, hidden_layer)完成从输入层到隐藏层的线性变换,然后经过激活函数及 torch.nn.Linear(hidden_layer, output_data)完成从隐藏层到输出层的线性变换。 + +torch.nn.Sequential 类是 torch.nn 中的一种序列容器,可以嵌套套各种实现神经网络中具体功能相关的类,来完成对神经网络模型的搭建,我们可以将嵌套在容器中的各个部分看作各种不同的模块,这些模块可以自由组合。模块的加入一般有两种方式,一种是在以上代码中使用的直接嵌套,另一种是以 orderdict 有序字典的方式进行传入,这两种方式的唯一区别是,使用后者搭建的模型的每个模块都有我们自定义的名字,而前者默认使 +用从零开始的数字序列作为每个模块的名字。 + +两者的区别如下所示: + +```python +hidden_layer = 100 +input_data = 1000 +output_data = 10 +models = torch.nn.Sequential( + torch.nn.Linear(input_data, hidden_layer), + torch.nn.ReLU(), + torch.nn.Linear(hidden_layer, output_data) +) +print(models) +``` + +输出结果如下: + +```text +Sequential( + (0): Linear(in_features=1000, out_features=100, bias=True) + (1): ReLU() + (2): Linear(in_features=100, out_features=10, bias=True) +) +``` + +使用orderdict的方式: + +```python +hidden_layer = 100 +input_data = 1000 +output_data = 10 + +from collections import OrderedDict +models = torch.nn.Sequential(OrderedDict([ + ('linear1',torch.nn.Linear(input_data,hidden_layer)), + ('relu',torch.nn.ReLU()), + ('linear2',torch.nn.Linear(hidden_layer,output_data)) +])) +print(models) +``` + +输出结果如下: + +```text +Sequential( + (linear1): Linear(in_features=1000, out_features=100, bias=True) + (relu): ReLU() + (linear2): Linear(in_features=100, out_features=10, bias=True) +) +``` + +下面简单对已经搭建好的模型进行训练和优化参数: + +```python +epoch_n = 10000 +learning_rate = 1e-4 +loss_fn = torch.nn.MSELoss() # 用在torch.nn中已经定义好的均方误差函数类来计算损失值 + +for epoch in range(epoch_n): + y_pred = models(x) + loss = loss_fn(y_pred,y) + # % 1000 == 0 每隔1000次输出一次 + if epoch % 1000 == 0: + print("Epoch:{},Loss:{:.4f}".format(epoch,loss.item())) + models.zero_grad() # 梯度清零 + loss.backward() # 反向传播 + for param in models.parameters(): + param.data -= param.grad.data * learning_rate +``` + +可以看出,参数的优化效果比较理想,loss 值被控制在相对较小的范围之内: + +```text +Epoch:0,Loss:1.0804 +Epoch:1000,Loss:0.9975 +Epoch:2000,Loss:0.9263 +Epoch:3000,Loss:0.8641 +Epoch:4000,Loss:0.8089 +Epoch:5000,Loss:0.7592 +Epoch:6000,Loss:0.7139 +Epoch:7000,Loss:0.6724 +Epoch:8000,Loss:0.6343 +Epoch:9000,Loss:0.5989 +``` + +#### torch.optim + +到目前为止,代码中的神经网络权重的参数优化和更新还没有实现自动化,并且目前使用的优化方法都有固定的学习速率,所以优化函数相对简单,如果我们自己实现一些高级的参数优化算法,则优化函数部分的代码会变得较为复杂。在 PyTorch 的 torch.optim 包中提供了非常多的可实现参数自动优化的类,比如 SGD、AdaGrad、RMSProp、Adam 等,这些类都可以被直接调用,使用起来也非常方便。 + +下面我们使用 torch.optim 中的Adam 类来实现对神经网络模型的参数优化: + +```python +import torch +from torch.autograd import Variable +batch_n = 100 +hidden_layer = 100 +input_data = 1000 +output_data = 10 + +x = Variable(torch.randn(batch_n,input_data),requires_grad=False) +y = Variable(torch.randn(batch_n,output_data),requires_grad=False) + +# 使用torch.nn搭建模型 +models = torch.nn.Sequential( + torch.nn.Linear(input_data,hidden_layer), + torch.nn.ReLU(), + torch.nn.Linear(hidden_layer,output_data) +) + +epoch_n = 100 +learning_rate = 1e-4 +loss_fn = torch.nn.MSELoss() # 用在torch.nn中已经定义好的均方误差函数类来计算 + +# 模型优化 +optimizer = torch.optim.Adam(models.parameters(),lr=learning_rate) + +for epoch in range(epoch_n): + y_pred = models(x) + loss = loss_fn(y_pred,y) + + if epoch % 10 == 0: + print("Epoch:{},Loss:{:.4f}".format(epoch,loss.item())) + + optimizer.zero_grad() # 梯度清零 + loss.backward() # 反向传播 + optimizer.step() # 更新参数 +``` + +这里使用了 torch.optim 包中的 torch.optim.Adam 类作为我们的模型参数的优化函数,在 torch.optim.Adam 类中输入的是被优化的参数和学习速率的初始值,如果没有输入学习速率的初始值,那么默认使用 0.001 这个值。因为我们需要优化的是模型中的全部参数,所以传递给 torch.optim.Adam 类的参数是 models.parameters。另外,Adam 优化函数可以对梯度更新使用到的学习速率进行自适应调节。 + +输出结果如下: + +```text +Epoch:0,Loss:1.1064 +Epoch:10,Loss:0.8949 +Epoch:20,Loss:0.7313 +Epoch:30,Loss:0.5999 +Epoch:40,Loss:0.4939 +Epoch:50,Loss:0.4058 +Epoch:60,Loss:0.3311 +Epoch:70,Loss:0.2681 +Epoch:80,Loss:0.2155 +Epoch:90,Loss:0.1714 +``` + +可以发现,仅仅使用了 100 次迭代,loss 值就已经降到了 0.17 左右,远低于之前进行10000次优化训练的结果,说明torch.optim效果非常理想。 + +## 手写数字识别实战 + +torchvision 包的主要功能是实现数据的处理、导入和预览等,所以如果需要对计算机视觉的相关问题进行处理,就可以借用在 torchvision 包中提供的大量的类来完成相应的工作。 + +其中,root 用于指定数据集在下载之后的存放路径,这里存放在根目录下的 data 文件夹中;transform 用于指定导入数据集时需要对数据进行哪种变换操作,;train 用于指定在数据集下载完成后需要载入哪部分数据,如果设置为 True,则说明载入的是该数据集的训练集部分;如果设置为 False,则说明载入的是该数据集的测试集部分。 + +```python +import torch +from torchvision import transforms,datasets +from torch.autograd import Variable # 用于将tensor转换成Variable来进行梯度计算 + + +# 下载训练集 MNIST 手写数字训练集 +data_train = datasets.MNIST(root="./data/", + transform=transforms.ToTensor(), + train=True, + download=True) +data_test = datasets.MNIST(root="./data/", + transform=transforms.ToTensor(), + train=False) +``` + +![20230527112128](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230527112128.png) + +### 数据增强data argumentation + +torch.transforms 中提供了丰富的类对载入的数据进行变换,比如说对图像进行旋转、裁剪、缩放、翻转等操作,这些操作可以增加数据集的多样性,从而提高模型的泛化能力。 + +| 函数名称 | 功能 | +| ----------------------------------------- | --------------------------------------------------------------------------- | +| torchvision.transforms.Resize | 按照需求大小进行缩放,比如说传入一个类似于(h,w)的序列,或者一个整数型数据) | +| torchvision.transforms.ToTensor | 对载入的图像数据进行类型转换,比如说PIL图片的数据转换成Tensor数据类型的变量 | +| torchvision.transforms.RandomVerticalFlip | 对输入图片按照随机概论水平翻转,默认的概率为0.5 | + +```python +transform=transform.Compose([transforms.ToTensor(), + transforms.Normalize(mean=[0.5,0.5,0.5],std=[0.5,0.5,0.5])]) +``` + +在 torchvision.transforms.Compose 中只使用了一个类型的转换变换transforms.ToTensor 和一个数据标准化变换 transforms.Normalize,标准化的计算公式如下: + +$$ +X^{normal} = \frac{X-mean}{std} +$$ + +### 数据预览和装载 + +在数据下载完成并且载入后,我们还需要对数据进行装载。我们可以将数据的载入理解为对图片的处理,在处理完成后,我们就需要**将这些图片打包好送给我们的模型进行训练**了,而装载就是这个打包的过程。在装载时通过 batch_size 的值来确认每个包的大小,通过 shuffle 的值来确认是否在装载的过程中打乱图片的顺序。 + +```python +# 数据装载 +data_loader_train = torch.utils.data.DataLoader(dataset=data_train, + batch_size=64, + shuffle=True) + +data_loader_test = torch.utils.data.DataLoader(dataset=data_test, + batch_size=64, + shuffle=True) +``` + +对数据的装载使用的是 torch.utils.data.DataLoader 类,类中的 dataset 参数用于指定我们载入的数据集名称,batch_size 参数设置了每个包中的图片数据个数,代码中的值是 64,所以在每个包中会包含 64 张图片。将 shuffle 参数设置为 True,在装载的过程会将数据随机打乱顺序并进行打包。 + +对数据进行预览如下: + +```python +# 数据预览 +images, labels = next(iter(data_loader_train)) +# iter()生成迭代器,next()返回迭代器的下一个项目 + +img = torchvision.utils.make_grid(images) +img = img.numpy().transpose(1,2,0) +std = [0.5,0.5,0.5] +mean = [0.5,0.5,0.5] +img = img*std+mean +print([labels[i] for i in range(64)]) +plt.imshow(img) +``` + +传递给utils.make_grid的参数是一个批次的数据,即64张图片,每个批次的装载数据都是4维的,即\[64,1,28,28\],其中64表示批次大小,1表示通道数,28表示图片的高度,28表示图片的宽度。而make_grid函数的作用是将批次的图片数据拼接成一张图片。得到的img维度变成(channel,height,width),即\[3,242,242\],其中3表示通道数,242表示图片的高度,242表示图片的宽度。 + +transpose对img进行转置并转换为numpy数组,即\[242,242,3\],这是因为想使用 Matplotlib 将数据显示成正常的图片形式,则使用的数据首先必须是数组,其次这个数组的维度必须是(height,weight,channel)。 + +输出结果如下: + +```text +[tensor(2), tensor(8), tensor(8), tensor(9), tensor(4), tensor(1), tensor(1), tensor(3), tensor(0), tensor(0), tensor(9), tensor(7), tensor(7), tensor(4), tensor(6), tensor(7), tensor(7), tensor(5), tensor(4), tensor(1), tensor(8), tensor(3), tensor(4), tensor(2), tensor(6), tensor(0), tensor(2), tensor(4), tensor(4), tensor(6), tensor(6), tensor(1), tensor(8), tensor(0), tensor(9), tensor(9), tensor(2), tensor(2), tensor(0), tensor(4), tensor(7), tensor(5), tensor(8), tensor(7), tensor(0), tensor(9), tensor(8), tensor(2), tensor(2), tensor(8), tensor(8), tensor(5), tensor(8), tensor(0), tensor(2), tensor(3), tensor(7), tensor(8), tensor(1), tensor(8), tensor(2), tensor(4), tensor(3), tensor(9)] +``` + +![20230527115022](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230527115022.png) + +### 模型搭建和参数优化 + +我们想要搭建一个包含了卷积层、激活函数、池化层、全连接层的卷积神经网络来解决这个问题。而这些可以通过torch.nn来实现,卷积层用torch.nn.Conv2d来实现,激活函数用torch.nn.ReLU来实现,最大池化层用torch.nn.MaxPool2d来实现,全连接层用torch.nn.Linear来实现。 + +```python +# 模型搭建和参数优化 +class Model(torch.nn.Module): + def __init__(self): + super(Model,self).__init__() # 调用父类的初始化函数,即先运行nn.Module的初始化函数 + self.conv1 = torch.nn.Sequential( + torch.nn.Conv2d(1,64,kernel_size=3,stride=1,padding=1), + torch.nn.ReLU(), + torch.nn.Conv2d(64,128,kernel_size=3,stride=1,padding=1), + torch.nn.ReLU(), + torch.nn.MaxPool2d(stride=2,kernel_size=2)) + + self.dense = torch.nn.Sequential( + torch.nn.Linear(14*14*128,1024), + torch.nn.ReLU(), + torch.nn.Dropout(p=0.5), + torch.nn.Linear(1024,10)) + + def forward(self,x): + x = self.conv1(x) + x = x.view(-1,14*14*128) + x = self.dense(x) + return x +``` + +torch.nn.Conv2d:用于搭建卷积神经网络的卷积层,主要的输入参数有输入通道 +数、输出通道数、卷积核大小、卷积核移动步长和 Paddingde 值。在这个简化的模型中,用了两个卷积层,第一个卷积层的输入通道数为 1,输出通道数为 64,卷积核大小为 3,卷积核移动步长为 1,Padding 值为 1。第二个卷积层的输入通道数为 64,输出通道数为 128,卷积核大小为 3,卷积核移动步长为 1,Padding 值为 1。 + +如何知道输入和输出通道数量? + +输出数据的通道数是由卷积核的数量决定的。卷积核的数量就是输出通道数 $C_{out}$,每个卷积核对应输出数据的一个通道。在训练过程中,每个卷积核会学习一组权重参数,用于提取输入数据的不同特征。因此,卷积层的输出通道数(即卷积核的数量)通常是作为网络的一个超参数来设定的,需要根据具体的任务和网络结构进行调整。 + +设输入数据的高度和宽度分别为 $H_{in}$ 和 $W_{in}$,卷积核的高度和宽度分别为 $H_k$ 和 $W_k$,步长为 $S$,填充大小为 $P$,则输出数据的高度和宽度分别为: + +$$ +H_{out} = \lfloor \frac{H_{in} + 2P - H_k}{S} \rfloor + 1 +$$ + +$$ +W_{out} = \lfloor \frac{W_{in} + 2P - W_k}{S} \rfloor + 1 +$$ + +torch.nn.ReLU: 用于搭建激活函数。 + +torch.nn.MaxPool2d: 用于搭建最大池化层,主要的输入参数有池化核大小和池化核移动步长(stride=2表示池化核移动步长为2)。 + +最大池化操作之后,输出数据的高度和宽度分别为: + +$$ +H_{out} = \lfloor \frac{H_{in} - H_k}{S} \rfloor + 1 +$$ + +$$ +W_{out} = \lfloor \frac{W_{in} - W_k}{S} \rfloor + 1 +$$ + +self.dense表示全连接层,输入的数据是二维的,即\[batch_size,14\*14\*128\],输出的数据是一维的,即\[batch_size,10\]。这里的14\*14\*128是通过计算得到的,具体的计算过程如下: + +第一个卷积层的输出数据的高度和宽度分别为: + +$$ +H_{out} = \lfloor \frac{H_{in} + 2P - H_k}{S} \rfloor + 1 = \lfloor \frac{28 + 2 - 3}{1} \rfloor + 1 = 28 +$$ + +$$ +W_{out} = \lfloor \frac{W_{in} + 2P - W_k}{S} \rfloor + 1 = \lfloor \frac{28 + 2 - 3}{1} \rfloor + 1 = 28 +$$ + +第二个卷积层的输出数据的高度和宽度分别为: + +$$ +H_{out} = \lfloor \frac{H_{in} + 2P - H_k}{S} \rfloor + 1 = \lfloor \frac{28 + 2 - 3}{1} \rfloor + 1 = 28 +$$ + +$$ +W_{out} = \lfloor \frac{W_{in} + 2P - W_k}{S} \rfloor + 1 = \lfloor \frac{28 + 2 - 3}{1} \rfloor + 1 = 28 +$$ + +再经过一次最大池化操作之后,输出数据的高度和宽度分别为: + +$$ +H_{out} = \lfloor \frac{H_{in} - H_k}{S} \rfloor + 1 = \lfloor \frac{28 - 2}{2} \rfloor + 1 = 14 +$$ + +$$ +W_{out} = \lfloor \frac{W_{in} - W_k}{S} \rfloor + 1 = \lfloor \frac{28 - 2}{2} \rfloor + 1 = 14 +$$ + +torch.nn.Linear: 用于搭建全连接层,主要的输入参数有输入特征数量和输出特征数量。在这个简化的模型中,用了两个全连接层,第一个全连接层的输入特征数量为 14\*14\*128,输出特征数量为 1024。第二个全连接层的输入特征数量为 1024,输出特征数量为 10。 + +全连接层的输出数据维度是由全连接层的神经元数量决定的。假设输入数据的维度为 $(N, C_{in})$,其中 $N$ 表示 batch size,$C_{in}$ 表示输入特征的数量。全连接层的神经元数量为 $C_{out}$,则全连接层的输出数据维度为 $(N, C_{out})$。 + +全连接层的运算过程可以看做是一个矩阵乘法加上偏置的操作。具体来说,全连接层的每个神经元都与输入数据的每个特征相连,它所对应的权重参数构成了一个大小为 $(C_{in}, 1)$ 的列向量。将所有神经元的权重参数按列组合起来,就可以得到一个大小为 $(C_{in}, C_{out})$ 的权重矩阵 $W$。然后,将输入数据 $X$ 与权重矩阵 $W$ 相乘,得到一个大小为 $(N, C_{out})$ 的输出矩阵 $Y$。最后,再将每个神经元的偏置参数加到对应的输出上,得到最终的输出数据。 + +需要注意的是,在深度学习中,全连接层通常用于将卷积层或者池化层的输出特征图展平为一个向量,并将其输入到后续的全连接层中。因此,在实际应用中,全连接层的神经元数量通常是根据前面卷积层或者池化层输出数据的维度来设定的。如果输入数据的维度较大,全连接层的神经元数量也会相应增加,以保证网络的表达能力。 + +torch.nn.Dropout类是为了防止训练的模型对各部分的权重参数产生依赖导致过拟合,随机丢弃神经元(参数归零),下图是对于这一过程的示意图: + +![20230527143550](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230527143550.png) + +前向传播forwardd 函数中的内容。首先,经过 self.conv1 进行卷积处 +理;然后进行 x.view(−1, 14*14*128),对参数实现扁平化,因为之后紧接着的就是全连接层,所以如果不进行扁平化,则全连接层的实际输出的参数维度和其定义输入的维度将不匹配,程序会报错;最后,通过 self.dense 定义的全连接进行最后的分类。 + +#### 模型训练和优化 + +```python +# 模型训练和优化 + +model = Model() +cost = torch.nn.CrossEntropyLoss() +optimizer = torch.optim.Adam(model.parameters()) +print(model) + +n_epochs = 5 +for epoch in range(n_epochs): + running_loss = 0.0 # 定义一个变量方便我们对loss进行输出 + running_correct = 0 # 定义一个变量方便我们计算预测对的数据个数,因为不是所有的预测都是100%正确的 + print("Epoch {}/{}".format(epoch,n_epochs)) + print("-"*10) # 用来分隔开输出结果 + for data in data_loader_train: + X_train,y_train = data + X_train,y_train = Variable(X_train),Variable(y_train) + # 将tensor转换成Variable,因为只有Variable才具有梯度 + + outputs = model(X_train) + _,pred = torch.max(outputs.data,1) # 找出每一行中最大值的下标,这是预测的分类结果 + + optimizer.zero_grad() + # 由于loss.backward()的梯度是累加的,所以每次运算完一个batch后记得清空梯度 + + loss = cost(outputs,y_train) # 计算损失值,注意传入的值是模型的输出值,而不是预测值 + + loss.backward() # loss进行反向传播,根据参数的梯度 + + optimizer.step() # 用来调整参数 + + running_loss += loss.data.item() # loss本身为Variable类型,所以要使用data获取其Tensor,因为其为标量,所以取0 + + running_correct += torch.sum(pred == y_train.data) # 计算预测对的数据个数 + testing_correct = 0 # 测试集中的预测正确的数量 + + for data in data_loader_test: + X_test,y_test = data + X_test,y_test = Variable(X_test),Variable(y_test) + outputs = model(X_test) + _,pred = torch.max(outputs.data,1) + testing_correct += torch.sum(pred == y_test.data) + print("Loss is:{:.4f},Train Accuracy is:{:.4f}%,Test Accuracy is:{:.4f}" + .format(running_loss/len(data_train),100*running_correct/len(data_train),100*testing_correct/len(data_test))) +``` + +训练输出结果如下: + +```text +Model( + (conv1): Sequential( + (0): Conv2d(1, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) + (1): ReLU() + (2): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) + (3): ReLU() + (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) + ) + (dense): Sequential( + (0): Linear(in_features=25088, out_features=1024, bias=True) + (1): ReLU() + (2): Dropout(p=0.5, inplace=False) + (3): Linear(in_features=1024, out_features=10, bias=True) + ) +) +Epoch 0/5 +---------- +Loss is:0.0023,Train Accuracy is:95.5283%,Test Accuracy is:98.3200 +Epoch 1/5 +---------- +Loss is:0.0008,Train Accuracy is:98.4700%,Test Accuracy is:98.5600 +Epoch 2/5 +---------- +Loss is:0.0005,Train Accuracy is:98.9300%,Test Accuracy is:98.2000 +Epoch 3/5 +---------- +Loss is:0.0004,Train Accuracy is:99.2467%,Test Accuracy is:98.7800 +Epoch 4/5 +---------- +Loss is:0.0003,Train Accuracy is:99.3900%,Test Accuracy is:98.8200 +``` + +### 随机验证 + +为了验证我们训练的模型是不是真的已如结果显示的一样准确,则最好的方法就是随机选取一部分测试集中的图片,用训练好的模型进行预测,看看和真实值有多大的偏差。 + +```python + +data_loader_test = torch.utils.data.DataLoader(dataset=data_test,batch_size=4,shuffle=True) # 一次4张图片 + +X_test,y_test = next(iter(data_loader_test)) # 读取一个batch的图片 +inputs = Variable(X_test) +pred = model(inputs) +_,pred = torch.max(pred,1) # 找到概率最大的下标 + +print("Predict Label is:",[i for i in pred.data]) +print("Real Label is:",[i for i in y_test]) + +img = torchvision.utils.make_grid(X_test) # 把图片拼成一个图 +img = img.numpy().transpose(1,2,0) # 把channel那一维放到最后 +std = [0.5,0.5,0.5] +mean = [0.5,0.5,0.5] +img = img*std+mean # 去标准化 +plt.imshow(img) +plt.show() +``` + +输出结果如下: + +![20230527153946](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230527153946.png) + +### 保存模型 + +```python +# 保存网络的参数 +torch.save(model.state_dict(),'./model/model.pth') +# 加载网络的参数 +# 首先定义模型 +model = Model() +model.load_state_dict(torch.load('./model/model.pth')) + +# 保存整个网络 +torch.save(model,'./model/model.pth') +# 加载整个网络 +model = torch.load('./model/model.pth') +``` + +pth是模型的参数文件,保存的是模型的参数,不包含模型的结构。 + +如果还想保存某一次训练采用的优化器、epochs等信息,可将这些信息组合起来构成一个字典,然后将字典保存起来: + +```python +state = {'model': model.state_dict(), 'optimizer': optimizer.state_dict(), 'epoch': epoch} +torch.save(state, './model/model.pth') +# 加载模型 +checkpoint = torch.load('./model/model.pth') # 加载断点 +model.load_state_dict(checkpoint['model']) # 加载模型可学习参数 +optimizer.load_state_dict(checkpoint['optimizer']) # 加载优化器参数 +epoch = checkpoint['epoch'] # 设置开始的epoch数 +``` + +### 模型的可视化 + +要对模型进行可视化,可以使用 torchsummary 库。这个库可以方便地打印出模型的结构和参数数量。 + +```python +from torchsummary import summary + +model = Model() +summary(model, (1, 28, 28)) # 输入数据的维度为 (batch_size, channels, height, width) +``` + +输出结果如下: + +```text +---------------------------------------------------------------- + Layer (type) Output Shape Param # +================================================================ + Conv2d-1 [-1, 64, 28, 28] 640 + ReLU-2 [-1, 64, 28, 28] 0 + Conv2d-3 [-1, 128, 28, 28] 73,856 + ReLU-4 [-1, 128, 28, 28] 0 + MaxPool2d-5 [-1, 128, 14, 14] 0 + Linear-6 [-1, 1024] 25,691,136 + ReLU-7 [-1, 1024] 0 + Dropout-8 [-1, 1024] 0 + Linear-9 [-1, 10] 10,250 +================================================================ +Total params: 25,775,882 +Trainable params: 25,775,882 +Non-trainable params: 0 +---------------------------------------------------------------- +Input size (MB): 0.00 +Forward/backward pass size (MB): 2.51 +Params size (MB): 98.33 +Estimated Total Size (MB): 100.84 +---------------------------------------------------------------- +``` + +还有一种可视化方法是使用 torchviz 库,这个库可以将模型的计算图可视化出来。这个需要下载Graphviz软件,然后将其添加到环境变量中,如 `C:\Program Files (x86)\Graphviz2.38\bin`。 + +```python +# 模型的可视化 +from torchviz import make_dot +x = Variable(torch.randn(1,1,28,28)) +y = model(x) +g = make_dot(y) +g.view() +``` + +输出结果如下: + +![20230527154944](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230527154944.png) diff --git "a/_posts/2023-06-01-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\344\271\235\357\274\211\342\200\224\342\200\224 \346\227\266\351\227\264\345\272\217\345\210\227\351\242\204\346\265\213- .md" "b/_posts/2023-06-01-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\344\271\235\357\274\211\342\200\224\342\200\224 \346\227\266\351\227\264\345\272\217\345\210\227\351\242\204\346\265\213- .md" new file mode 100644 index 0000000000..b0f27aac5d --- /dev/null +++ "b/_posts/2023-06-01-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\344\271\235\357\274\211\342\200\224\342\200\224 \346\227\266\351\227\264\345\272\217\345\210\227\351\242\204\346\265\213- .md" @@ -0,0 +1,11 @@ +--- +layout: post +title: 深度学习与图像处理(九)—— 时间序列预测 +date: 2023-06-01 +tag: 机器学习 +--- +## 一、时间序列 + +### 1.1 时间序列的概念 + +时间序列是指在一定时间间隔下,某个量的数值按照时间先后顺序排列的数值序列。时间序列分析是根据时间序列的历史数据对未来进行预测的一种方法。时间序列分析的目的是对时间序列进行预测,以便在未来的某个时间点上对所研究的现象进行干预。 diff --git "a/_posts/2023-06-01-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\345\205\253\357\274\211- \350\277\201\347\247\273\345\255\246\344\271\240.md" "b/_posts/2023-06-01-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\345\205\253\357\274\211- \350\277\201\347\247\273\345\255\246\344\271\240.md" new file mode 100644 index 0000000000..bf884f744d --- /dev/null +++ "b/_posts/2023-06-01-\346\267\261\345\272\246\345\255\246\344\271\240\344\270\216\345\233\276\345\203\217\345\244\204\347\220\206\357\274\210\345\205\253\357\274\211- \350\277\201\347\247\273\345\255\246\344\271\240.md" @@ -0,0 +1,123 @@ +--- +layout: post +title: 深度学习与图像处理(八)- 迁移学习 +date: 2023-06-01 +description: "深度学习与图像处理(八)- 迁移学习" +tag: 机器学习 +--- +## 1. 什么是迁移学习? + +在之前的深度学习用于手写数字的识别中,我们搭建好深度神经网络模型后,需要花费大量的算力和时间去训练模型和优化参数,如果得到的模型只能解决这一个问题,性价比非常低。如果能对一个训练好的模型进行细微调整,就能将其应用到相似的问题中;另外,对于原始数据较少的问题,采用迁移学习方法也能得到较好的效果。 + +在使用迁移学习的过程中有时会导致迁移模型出现负迁移,我们可以将其理解为模型的泛化能力恶化。假如我们将迁移学习用于解决两个毫不相关的问题,则 +极有可能使最后迁移得到的模型出现负迁移。 + +## 2. 迁移 VGG16 + +在这里我们使用迁移学习来解决猫狗分类问题,我们使用的数据集是 kaggle 上的猫狗分类数据集,下载地址:https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition/data + +这时候,我们不需要搭建自己的模型,而是通过下载 VGG16 模型,然后在其基础上进行迁移学习。 + +![20230602085337](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230602085337.png) + +### 2.1 下载 VGG16 模型 + +```python +import torch +import torchvision +from torchvision import datasets,models,transforms +import os +from torch.autograd import Variable +import matpolib.pyplot as plt +import time + +model = models.vgg16(pretrained=True) +``` + +尽管迁移学习要求我们需要解决的问题之间最好具有很强的相似性,但是每个问题对最后输出的结果会有不一样的要求,而承担整个模型输出分类工作的是卷积神经网络模型中的全连接层,所以 +在迁移学习的过程中调整最多的也是全连接层部分。其基本思路是冻结卷积神经网络中全连接层之前的全部网络层次,让这些被冻结的网络层次中的参数在模型的训练过程中不进行梯度更新,能够被优化的参数仅仅是没有被冻结的全连接层的全部参数。 + +比如说,迁移过来的VGG16架构模型最后的输出结果是1000个,而我们的猫狗识别问题只需要输出2个,所以我们需要将最后的全连接层进行修改,使其输出2个结果。 + +```python +for parma in model.parameters(): + parma.requires_grad = False +model.classifier = nn.Sequential( + torch.nn.Linear(25088, 4096), + torch.nn.ReLU(), + torch.nn.Dropout(p=0.5), + torch.nn.Linear(4096, 4096), + torch.nn.ReLU(), + torch.nn.Dropout(p=0.5), + torch.nn.Linear(4096, 2) +) + +cost = torch.nn.CrossEntropyLoss() +optimizer = torch.optim.Adam(model.classifier.parameters()) +print(model) +``` + +在这段代码中,对原模型中的参数进行遍历操作,但是将参数中的parma.requires_grad设置为False,这样就可以将这些参数冻结起来,不参与训练。 + +然后定义新的全连接层结构并重新赋值给model.classifier,最后定义损失函数和优化器。在完成了新的全连接层定义之后,全连接层的parma.requires_grad默认为True,所以在训练的过程中,只有全连接层的参数会被更新。 + +新的分类器是一个由三个全连接层组成的序列,其中第一个全连接层的输入大小为25088,输出大小为4096;第二个全连接层的输入大小为4096,输出大小为4096;第三个全连接层的输入大小为4096,输出大小为2,表示最终分类的类别数目为2。每个连接层后面都跟着一个ReLU激活函数和一个Dropout层,Dropout层的参数p设置为0.5,表示在训练过程中每个神经元有50%的概率被随机丢弃,这样可以有效地防止过拟合。 +输出结果如下: + +```text +VGG( + (features): Sequential( + (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) + (1): ReLU(inplace=True) + (2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) + (3): ReLU(inplace=True) + (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) + (5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) + (6): ReLU(inplace=True) + (7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) + (8): ReLU(inplace=True) + (9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) + (10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) + (11): ReLU(inplace=True) + (12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) + (13): ReLU(inplace=True) + (14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) + (15): ReLU(inplace=True) + (16): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) + (17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) + (18): ReLU(inplace=True) + (19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) + (20): ReLU(inplace=True) + (21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) + (22): ReLU(inplace=True) + (23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) + (24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) + (25): ReLU(inplace=True) + (26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) + (27): ReLU(inplace=True) + (28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) + (29): ReLU(inplace=True) + (30): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) + ) + (avgpool): AdaptiveAvgPool2d(output_size=(7, 7)) + (classifier): Sequential( + (0): Linear(in_features=25088, out_features=4096, bias=True) + (1): ReLU() + (2): Dropout(p=0.5, inplace=False) + (3): Linear(in_features=4096, out_features=4096, bias=True) + (4): ReLU() + (5): Dropout(p=0.5, inplace=False) + (6): Linear(in_features=4096, out_features=2, bias=True) + ) +) +``` + +### 模型训练 + +```python +data_dir = './data' +data_trans + +``` + +data_transform中,定义了数据预处理的操作,包括将图像尺寸调整为[224, 224],将图像转换为张量,并对每个通道进行归一化(均值为0.5,方差为0.5)。这些操作都被包含在了transforms.Compose中。 diff --git "a/_posts/2023-06-10-\346\225\260\346\215\256\346\217\222\350\241\245\346\274\253\350\260\210.md" "b/_posts/2023-06-10-\346\225\260\346\215\256\346\217\222\350\241\245\346\274\253\350\260\210.md" new file mode 100644 index 0000000000..4dfeb85296 --- /dev/null +++ "b/_posts/2023-06-10-\346\225\260\346\215\256\346\217\222\350\241\245\346\274\253\350\260\210.md" @@ -0,0 +1,253 @@ +--- +layout: post +title: 数据插补漫谈 +date: 2023-06-10 +tags: 数据分析 +--- +## 1. 风电场数据基本特点 + +由于机械故障、天气因素和人为影响等原因,风场内风速数据出现采集时间短、间断点多、数据失真等诸多问题,给风资源的评估带来不小的麻烦。 + +### 1.1 风电场数据基本类型 + +风电场内的数据根据数据来源情况可分为两类:一类是风电机组运行数据,即SCADA机组数据;另一类是风资源测量数据,主要是测风塔测量数据,测风塔测量成本低,可靠性高,在风电场前期规划与后期运行过程中占据地位。多普勒激光雷法也可以测量风速,但因为成本过高在风电场中一些高精度研究项目才会使用。[^1] + +#### 1.1.1 SCADA数据 + +SCADA数据记录了风电机组运行期间的各种详细数据,包括风向、风速、温度等环境参数,以及输出功率、电流、电压等机组功率参数,桨距角。偏航等发电控制策略参数以及各种转速等运行监测数据。[^1] 其中,比较重要的是**机舱风速**和**输出功率**。 + +#### 1.1.2 测风塔数据 + +是当地风资源的主要数据来源,包括当地长期的风速、风向、温度等环境参数。测风塔数据的采集周期一般为10min,数据采集不止一个高度,数据量较大。[^1] + +#### 1.1.3 激光雷达测风数据 + +激光雷达的测量以激光脉冲多普勒频移原理为基础,是一种测量精度高但是成本高的测风方式,可以测得几十米到几百米范围内多层高的风速、风向、温度等数据,采样频率高,数据层数多,但是由于成本较高,一般得到的都是几个月长度的短期数据。[^1] + +#### 1.1.4 其他数据 + +其他气象数据还包括气象站点数据、卫星数据、模型数据以及再分析数据集。 + +其中,再分析数据集如CFSR,ERA-5,ERA-Interim, MERRA-2, NCEP/NCAR Reanalysis I等数据集,是一种基于数值模式并且同化融合了实测数据的再分析数据集,其数据时间跨度长,空间分辨率高,数据质量高,是风资源评估中常用的数据来源。 + +### 1.2 风电场数据缺失情况 + +#### 1.2.1 机组运行数据缺少情况 + +从缺失数据分布来看,主要存在以下几种情况: + +1. 离散缺失:在某些时刻数据出现缺失情况,缺失位置的前后时间与同时刻机组数据正常; +2. 少量缺失:少量机组同时出现数据缺少的情况,通常是由于通信问题造成的; +3. 严重缺失:同一时间大量机组数据缺少,通常是由于机组停机造成的。 + +考虑到风电场内不同机组的风速具有较大的相关性,当运行风速数据出现离散缺失和少量缺失的情况下,可以使用其他机组的风速对缺失数据进行插补和重构。 + +#### 1.2.2 风电场数据缺失原因 + +### 1.3 除了数据缺失之外其他需要插补的原因 + +除了测量仪器故障之外,在工程设计时,有部分项目由于工程进度要求,在未收集到一个完整年测风数据的情况下就需完成对风电场风能资源及发电量的评估,为保障评估结果的合理性,也要对不满一年的测风数据进行插补订正。[^4] + +同时,根据《风电场工程风能资源测量与评估技术规范》(NB/T 31147-2018),测风塔所选时间段的有效数据完整率应达到90%以上,测风时长满足连续一个完整年,因此对于缺测或者无效的数据需要采取插补的方法以提高数据完整率和测风时长。[^7] + +## 2. 数据插补方法 + +### 2.1 传统风速插补方法 + +现阶段风电行业内采用基于相关测量预测方法(MCP,Measure-Correlate Predict)(可称之为传统插补方法)进行间断数据的插补和拟合。 + +传统的插补方法包括测量相关预测方法(Measure-correlate-predict,MCP)[^2],它主要是利用目标站点短期测风数据(风速、风向)以及长期参考数据预测目标站点长期风况。该方法可以利用参考数据修正测风期间由于仪器受损、人为因素等原因造成的数据缺失。 + +目标站点一般是测风塔的测风数据,参考数据选择目标站点附近与其地形相似且气候条件相近、风能资源分布相似、长时间(10年以上的)实测气象数据和再分析数据。 + +有如下八种MCP算法:线性回归法、正交回归法、矩阵时间序列法、快速排序法、方差比法、风速比法、垂直分层算法以及 威布尔拟合算法。 + +![20230616113457](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230616113457.png) [^8] + +
主流的MCP算法原理图
+ +也就是说,MCP算法第一步就是建立参考站点和目标站点的关联模型,其中,线性回归法和方差比法是最常用的两种方法。 + +#### 2.1.1 线性回归法 + +也叫作线性最小二乘法(LLS),LLS算法的拟合曲线是一条拟合曲线: + +$$ +\left\{\begin{array}{c} +y=m x+b \\ +m=S_{x y} / S_{x x} \\ +b=\bar y-m \bar x \\ +S_{x y}=\sum_{i=1}^{n} (x_{i} - \bar x)(y_{i} - \bar y) \\ +S_{x x}=\sum_{i=1}^{n} (x_{i} - \bar x)^{2} + +\end{array}\right. +$$ + +y代表线性回归得到的目标站风速,x代表参考站风速,m和b分别代表线性回归的斜率和截距,$S_{x y}$和$S_{x x}$分别代表参考站风速和目标站风速的协方差和方差。 + +#### 2.1.2 方差比法 + +采用线性回归的方法时,得到的目标站风速的预测值和实际值之间比较接近,但是预测值的方差会远远小于观测得到目标站的方差,这导致了风速分布的不准确。为了解决这个问题,方差比法被提出。 + +### 其他统计方法 + +#### 2.2.2 三次样条插值法 + +缺点:自然风速时间序列是一个波动强烈的非线性时间序列,Lagrange、三次样条和线性插值等传统插值算法中,任意两个信息点之间用直线或者光滑的曲线连接,掩盖了相邻插值点间的局部变化特征,因此用于波动强烈的风速时间序列效果并不理想(用于频率相对稳定的低频和中频曲线插值效果比较理想)。[^3] + + + +### 2.3 物理模型插补方法 + +物理模型通过发现、归纳和求解规律的方式实现风速的预测。如大气运动基本方程组,可以通过求解方程组得到风速的预测值。 + +气象特征包括风向、温度、相对湿度、露点温度、气压和海平面气压。这些 因素与风速的时序变化存在复杂的非线性相关性的因素。因此,气象特征的预测是风速预测的重要组成部分。 + +$$ +\frac{\partial u}{\partial t} + u \frac{\partial u}{\partial x} + v \frac{\partial u}{\partial y} + w \frac{\partial u}{\partial z} = - \frac{1}{\rho} \frac{\partial p}{\partial x} + f v + \nu \nabla^2 u +$$ + +$$ +\frac{\partial v}{\partial t} + u \frac{\partial v}{\partial x} + v \frac{\partial v}{\partial y} + w \frac{\partial v}{\partial z} = - \frac{1}{\rho} \frac{\partial p}{\partial y} - f u + \nu \nabla^2 v +$$ + +$$ +\frac{\partial p}{\partial z} = - \rho g +$$ + +$$ +\frac{\partial T}{\partial t} + u \frac{\partial T}{\partial x} + v \frac{\partial T}{\partial y} + w \frac{\partial T}{\partial z} = \frac{1}{\rho c_p} \left( \frac{\partial q}{\partial x} + \frac{\partial q}{\partial y} + \frac{\partial q}{\partial z} \right) + \nu \nabla^2 T +$$ + +$$ +p = \rho R T +$$ + +### 2.4 分形维数插补方法 + +分形理论作为非线性科学中的一个分支,可以定性或定量分析不规则的几何 体和复杂现象。钟莉等从全国80个典型站点风速记录的关联维数统计得知,风速时程的分形维数均约在1.7左右。[^6] +利用分形的理论和分析方法对测风场的时间序列进行描述和研究,可以更好地揭示风场的内在规律,从而为风电场的设计和运行提供理论依据。[^5] + +### 2.5 机器学习插补方法 + +机器学习的主要任务之一是分类问题,它的目标是将输入数据分成不同的类别。分类问题通常用于识别图像、语音、文本等数据类型。另一个主要任务是回归问题,它的目标是预测一个连续的输出变量。回归问题通常用于预测房价、股票价格、销售量等连续变量。因此,使用机器学习进行数据插补属于回归问题。 + +#### 2.5.1 KNN + +一种基本的分类方法是 k 最近邻 (kNN) 算法。类成员是 k-NN 分类的结果。 + +项目的分类取决于它与训练集中的点的相似程度,该对象将进入其 k 个最近邻中成员最多的类。如果 k = 1,则该项目被简单地分配给该项目最近邻居的类。使用缺失数据找到与观测值最近的 k 邻域,然后根据邻域中的非缺失值对它们进行插补可能有助于生成关于缺失值的预测。 + +#### 2.4.2 随机森林 + +随机森林 + +#### 2.5.3 支持向量回归 + +### 2.6 时间序列模型插补法 + +常用的平稳时间序列模型主要包括:自回归模型(AR模型)、移动平均模型(MA)和自回归移动平均模型(ARMA)。建立AR,MA,ARMA,ARIMA模型,对风速进行预测,然后将预测值填充到缺失值中。 + +### 2.7 多重插补法 + +多重插补(Mutiple imputation,MI)的思想来源于贝叶斯估计,认为待插补的值是随机的,它的值来自于已观测到的值。具体实践上通常是估计出待插补的值,然后再加上不同的噪声,形成多组可选插补值。根据某种选择依据,选取最合适的插补值。 + +多重插补法大致分为三步: + +为每个空值产生一套可能的插补值,这些值反映了模型的不确定性;每个值都被用来插补数据集中的缺失值,产生若干个完整数据集合。 +每个插补数据集合都用针对完整数据集的统计方法进行统计分析。 +对来自各个插补数据集的结果进行整合,产生最终的统计推断,这一推断考虑到了由于数据插补而产生的不确定性。该方法将空缺值视为随机样本,这样计算出来的统计推断可能受到空缺值的不确定性的影响。 + +#### 2.7.1 MICE简介 + +通过链式方程进行的多元插补(MICE,Multiple Imputation by Chained Equations),与单个插补(例如均值)相比,创建多个插补可解决缺失值的不确定性。MICE假定缺失的数据是随机(MAR)的,这意味着,一个值丢失概率上观测值仅取决于并且可以使用它们来预测。通过为每个变量指定插补模型,可以按变量插补数据。 + +### 2.8 深度学习插补方法 + +#### 2.5.1 DNN + +#### 2.5.2 CNN + +#### 2.5.3 GRU + +#### 2.5.4 LSTM + +#### 2.5.5 MLP + +#### 2.5.6 VAE + +变分自编码器(Variational Auto-Encoders,VAE)作为深度生成模型的一种形式,是由 Kingma 等人于 2014 年提出的基于变分贝叶斯(Variational Bayes,VB)推断的生成式网络结构。与传统的自编码器通过数值的方式描述潜在空间不同,它以概率的方式描述对潜在空间的观察,在数据生成方面表现出了巨大的应用价值。 + +工作思路如下: + +比如说。利用最邻近机组风速,选取两台风速相关性最大的得到一个两列的风速序列,并且每行数据对应时间相同:将这两列数数据划分为训练集与测试集,以训练集中第一台机组风速作为输入,第二台机组风速作为输出对模型进行训练;模型训练完成后,将测试集中第一台机组风速输入模型,输出第二台机组风速的重构序列,重构序列与测试集中第二台机组风速具有相同的数据特征:对测试集中第二台机组风速序列进行污染,使用重构序列对应位置数据对污染后的序列进行填充,达到数据插补的目的。主要是是利用同一风场不同机组间风速的空间相关性,这主要是应对离散缺失和少量缺失的情况。[^1] + +对于数据缺失比较严重的情况,可以考虑使用LSTM神经网络对风速序列进行重构:基本工作思路如下,首先得到一个风速数据的时间序列,在序列中添加噪声获得大段时间缺失后的风速序列,使用LSTM对风速序列进行重构,得到一组新的风速序列,该序列与原始数据具有相同的数据特征,可以评估插补效果。[^1] + +## 3. 插补方法的评价指标 + +为了衡量数据插补的效果,分析插补后的数据和真实值之间的偏差,需要定义一些评价指标。 + +### 3.1 插补方法的评价指标 + +偏差 Bias: + +$$ +Bias = \frac{1}{n} \sum_{i=1}^n (y_i - \hat{y}_i) +$$ + +平均绝对误差 mean absolute error (MAE): + +$$ +MAE = \frac{1}{n} \sum_{i=1}^n |y_i - \hat{y}_i| +$$ + +范围[0,+∞),当预测值与真实值完全吻合时等于0,即完美模型;误差越大,该值越大。 + +均方误差 mean squared error (MSE): + +$$ +MSE = \frac{1}{n} \sum_{i=1}^n (y_i - \hat{y}_i)^2 +$$ + +范围[0,+∞),当预测值与真实值完全吻合时等于0,即完美模型;误差越大,该值越大。 + + +均方根误差 root mean squared error (RMSE): + +$$ +RMSE = \sqrt{\frac{1}{n}\sum_{i=1}^n (y_i - \hat y_i)^2} +$$ + +范围[0,+∞),当预测值与真实值完全吻合时等于0,即完美模型;误差越大,该值越大。 + +纳什效率系数 Nash-Sutcliffe efficiency (NSE): + +$$ +NSE = 1 - \frac{\sum_{i=1}^n (y_i - \hat y_i)^2}{\sum_{i=1}^n (y_i - \bar y)^2} +$$ + +其中,$\hat y_i$为预测值,$y_i$为真实值,n为样本数,$\bar y$为真实值的均值。 + +## 4. 结论与展望 + +* 风电场测风数据主要包括测风塔数据和激光雷达数据,测风塔数据完整性较好,激光雷达数据测量精度高,测量高度范围打,但是存在测量时间短和高层数据缺失量大的问题。 +* 不同地形风电场都存在数据缺失的情况,内陆平坦地形风电场主要是少量缺失与严重缺失情况;复杂山地地形风电场同时存在离散缺失和严重缺失情况。 + +## 5.参考文献 + +[^1]: [风电场运行风速数据清洗与重构方法研究](https://kns.cnki.net/kcms2/article/abstract?v=3uoqIhG8C475KOm_zrgu4lQARvep2SAkaWjBDt8_rTOnKA7PWSN5MP_gAQDb0zp3pwdYqK_qH1hV-PcN01LICOyomuvs_blk&uniplatform=NZKPT) + +[^2]: [不同MCP方法在平原及山地风电项目中的插补应用](https://kns.cnki.net/kcms2/article/abstract?v=3uoqIhG8C44YLTlOAiTRKibYlV5Vjs7i8oRR1PAr7RxjuAJk4dHXouke398ON9xxtn24hpZDL59m9j6UlQFFCjFLb083EHhI&uniplatform=NZKPT) + +[^3]: [分形插值在风速时间序列中的应用](https://kns.cnki.net/kcms2/article/abstract?v=RtZjjXMhIaUNGcuPaxQfbDcC2yalzWoCL93piogULQeXwMTSaEY1uUYwPd3rV3HfZylURiJR4YXfuvEv8rRkZwfXC-qaCNyhKrwhTztXciiL6LRITbIlG0WMSghT8PrgCf288mCBws_sz5snXmokJeIDUMyXvhwc&uniplatform=NZKPT) + +[^4]: [风电场测风数据插补方法适用性研究](https://kns.cnki.net/kcms2/article/abstract?v=3uoqIhG8C44YLTlOAiTRKibYlV5Vjs7iy_Rpms2pqwbFRRUtoUImHYpiOGNT2qlSARG-j2VOy5qT-TS-gL8chcqtg5-v_WPu&uniplatform=NZKPT) + +[^5]: [风场分形特性及缺失数据插补研究](https://kns.cnki.net/kcms2/article/abstract?v=3uoqIhG8C475KOm_zrgu4lQARvep2SAkVR3-_UaYGQCi3Eil_xtLbyXOrOGjFzEEUpul5ls54JYzFFNPB8OrNX-V67MQlV9O&uniplatform=NZKPT) + +[^6]: [风速时程的分形特征分析](https://kns.cnki.net/kcms2/article/abstract?v=3uoqIhG8C44YLTlOAiTRKgchrJ08w1e7VSL-HJEdEx0tMvpN0xWyB2A7Bndh63wTbQpvDkIouUiX_x4XPZbwOxCGUmv529eQ&uniplatform=NZKPT) + +[^7]: [风电场测风数据插补的不确定度研究](https://kns.cnki.net/KXReader/Detail?invoice=bf2qg1kdTMCiR2EnIuxNmDLH76z8I9YamyAStKBBBfGdlX1pHdoxpiRIG7%2BlwU2vOm74hquenLlDIaRkVUGVqJr6U9fXXtO3%2FfVtyR6MMNNNAyucHIitEh2uw6FL3My3Epb9UpyU5SJvYm1z8h72LtBBJWkpYcendGzjmh%2B2pyg%3D&DBCODE=CJFD&FileName=NCNY202305008&TABLEName=cjfdauto&nonce=56D811CC2E874766A4AC3DC0EE0A7B4E&TIMESTAMP=1686648324986&uid=) + +[^8]: [基于神经网络风电场测风数据插补与长年代风速推算方法研究](https://kns.cnki.net/kcms2/article/abstract?v=3uoqIhG8C475KOm_zrgu4lQARvep2SAkVtq-vp-8QbjqyhlE-4l1YjUpam0s47n2V_s6zjEhcfKUjf0hNESc1qiHj-IQvXTg&uniplatform=NZKPT) diff --git "a/_posts/2023-06-12-\351\243\236\350\205\276\345\267\245\346\216\247\346\234\272\345\256\211\350\243\205ubuntu.md" "b/_posts/2023-06-12-\351\243\236\350\205\276\345\267\245\346\216\247\346\234\272\345\256\211\350\243\205ubuntu.md" new file mode 100644 index 0000000000..b8a3ef26e6 --- /dev/null +++ "b/_posts/2023-06-12-\351\243\236\350\205\276\345\267\245\346\216\247\346\234\272\345\256\211\350\243\205ubuntu.md" @@ -0,0 +1,445 @@ +--- +layout: post +title: 飞腾工控机安装ubuntu +date: 2023-06-12 +tag: Linux +--- +## 1. 安装Ubuntu + +### 1.1 下载镜像 + +操作系统是ubuntu20.04server版本,可以在该[镜像](https://mirrors.cloud.tencent.com/ubuntu-cdimage/releases/20.04.3/release/ubuntu-20.04.5-live-server-arm64.iso)地址下载。 + +### 1.2 制作启动盘 + +U盘启动盘工具rufus下载地址:[rufus](https://rufus.ie/zh/) + +Rufus制作启动盘的步骤如下: + +设备一栏选择的是我们准备要用的U盘 + +选择,引导类型选型,找到所选系统的iso文件 + +其他默认即可,点击开始安装 + +rufus在开始安装之后,会弹出对话框询问是采用"ISO Image"还是"DD"模式,"DD"模式安装成功率更高,但是制作的启动盘不能被window系统所识别;后面会询问是否需要"格式化磁盘",这一步会擦除U盘上的所有数据。 + +![图片1](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/图片1.png) + +## 2. 安装驱动 + +如果需要用USB无线网卡则需要下载几个支持的工具: +http://ports.ubuntu.com/pool/main/w/wpa/wpasupplicant_2.9-1ubuntu4_arm64.deb +http://ports.ubuntu.com/pool/main/libn/libnl3/libnl-route-3-200_3.4.0-1_arm64.deb +http://ports.ubuntu.com/pool/main/p/pcsc-lite/libpcsclite1_1.8.26-3_arm64.deb + +## 3. 安装后的配置 + +对于ubuntu 20.04,初次安装的grub有问题,无法显示启动菜单,需要修改配置让grub以文本模式运行,**显示倒计时,编辑Grub的配置文件/etc/default/grub** ,去掉下面一行最前面的注释字符“#”: + +如果您想使用磁盘上剩余的空间,可以在磁盘上创建一个新的分区,并将其挂载到文件系统中 + +## 4. 设置网络 + +### 4.1 驱动安装 + +#### 4.1.1 使用飞腾CPU自带网卡 + +支持飞腾CPU的内核可以完成飞腾自带网卡的驱动, **这种内核一般是在其它机器上编译完内核后打包成的*** **.deb格式的安装文件,分成内核和内核头文件两个包** 。用优盘将两个安装包拷贝到系统中,然后执行dpkg命令安装: + +```shell +dpkg -i linux-headers-4.19.6-phy_arm64.deb +dpkg -i linux-image-4.19.6-phy_arm64.deb +``` + +然后重启设备,在grub菜单中选择”Advance option..." , -> "ubuntu, with linux 4.19.6-phy",等待启动,由于这个内核包含的驱动较少,所以启动时会显示很多内容不支持,甚至可能会卡住数十秒不动。 + +### 4.1.2 免驱动网卡 + +先用ip link show功能查看系统内现有的网卡,一般只有一个名为“lo”环回接口。然后插入上述网卡(虽然USB口网卡支持热插拔,为稳妥起见,建议重启设备),然后用上述命令再次查看网络接口,如果有新增的,即说明网口驱动成功。 + +#### 4.1.3 无线网卡驱动安装 + +无线网卡由于有一个与无线路由器之间的鉴权机制,所以还需要一个叫作wpa_supplicant的软件包支持。需要从网上下载以下文件并用优盘拷贝到本设备安装。 + +```shell +dpkg -i ./libnl-route-3-200_3.4.0-1_arm64.deb +dpkg -i ./libpcsclite1_1.8.26-3_arm64.deb +dpkg -i ./wpasupplicant_2.9-1ubuntu4_arm64.deb +``` + +### 4.2 网卡设置IP地址 + +各类型网卡驱动成功后,就需要配置IP地址,可以先临时配置一个IP地址,先ping一下网关看是否通,然后再配置正式的地址。输入ip link show命令查看,如果除了lo环回网卡外,还能看到其它序号从2开始的其它网卡,说明网卡驱动成功了。这里以飞腾CPU自带网卡为例,看到的内容如下,网卡名称为enaphyt4i0和enaphyt4i1。 + +#### 4.2.1 临时动态IP地址 + +```shell +dhclient enaphyt4i0 +dhclient enaphyt4i1 +``` + +#### 4.2.2 静态IP地址设置 + +使用netplan固定IP。 + +## 5 docker安装 + +```shell +sudo apt-get install ca-certificates curl gnupg lsb-release +``` + +安装docker + +```shell +apt-get install -y docker.io +``` + +启动服务: + +```shell +systemctl start docker +``` + +设置开机启动docker + +```shell +systemctl enable docker +``` + +### 安装arm架构的ubuntu + +[Getting started with Docker for Arm on Linux +](https://www.docker.com/blog/getting-started-with-docker-for-arm-on-linux/) + +[Install Docker Engine on Ubuntu +](https://docs.docker.com/engine/install/ubuntu/) + +卸载旧的版本: +```shell +sudo apt-get purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras +``` + +```shell +curl -fsSL test.docker.com -o get-docker.sh && sh get-docker.sh +``` + +-o get-docker.sh 指定输出文件名为get-docker.sh + +&& sh get-docker.sh 执行get-docker.sh + + +启动docker + +``` +systemctl enable docker +#启动Docker服务: +systemctl start docker +``` + +## 6. 编译内核 + +安装编译必须的程序和库 + +```shell +apt install build-essential libncurses5-dev bison flex libssl-dev +``` + +解压缩内核源码 + +```shell +tar -zxvf linux-4.19.tar.gz +``` + +打补丁 + +```shell +cd linux-4.19 +patch -p1 < ../patch-phytium-4.19.11 +``` + +patch-phytium-4.19.11.rar文件的解压缩如下: + +```shell +sudo apt-get install rar +sudo apt-get install unrar +unrar x patch-phytium-4.19.11.rar +``` + +内核的配置选项极多,内核补丁给内核添加了几个适合飞腾CPU的缺省的配置文件,可以使用修改好的config_d2000_linux-4.19-example进行缺省配置: + +```shell +cp ../config_d2000_linux-4.19-example .config +make oldconfig +``` + +如果当前内核的版本与以前配置的内容有差异,会提示用户输入差异的处理方式,是编译或者不编译,所以这种方法适合参考的配置文件与当前内核版本差别小的情况,否则手工输入大量的差异将是很繁琐的事情。 + +然后运行如下命令: + +```shell +make menuconfig +``` + +```shell +make -j8 # lscpu查看cpu核心数 +``` + +这段代码含义是:编译内核,使用8个线程,编译内核模块,编译完成后安装内核模块,安装内核模块的路径为/lib/modules/4.19.11,安装内核的路径为/boot/vmlinuz-4.19.11,安装内核的配置文件为/boot/config-4.19.11,安装内核的系统映像文件为/boot/System.map-4.19.11,安装内核的头文件为/usr/include/linux-4.19.11。 + +编译完成之后安装驱动模块: + +```shell +make modules_install +``` + +安装内核: + +```shell +make install +``` + +### 修改启动项 + +```shell +vim /etc/default/grub +``` + +新编译的内核会自动加入grub启动菜单,下次启动时可手工选择新编译的内核,如果没有启动成功,还可以选择缺省的旧内核。如果启动成功,可以修改grub的配置文件/etc/default/grub,找到下面一行 +GRUB_DEFAULT=0 +把最后的数字0改为欲启动的选项的编号,例如1。修改完后执行update-grub更新启动配置。 + +## 7. 问题 + +### netplan问题 + +``` +** (generate:5165): WARNING **: 06:56:46.538: Problem encountered while validating default route consistency.Please set up multiple routing tables and use `routing-policy` instead. +Error: Conflicting default route declarations for IPv4 (table: main, metric: default), first declared in enaphyt4i1 but also in enaphyt4i0 + +** (process:5163): WARNING **: 06:56:47.013: Problem encountered while validating default route consistency.Please set up multiple routing tables and use `routing-policy` instead. +Error: Conflicting default route declarations for IPv4 (table: main, metric: default), first declared in enaphyt4i1 but also in enaphyt4i0 +``` + +配置文件如下: + +```yaml +# This is the network config written by 'subiquity' +network: + version: 2 + renderer: networkd + ethernets: + enaphyt4i0: + optional: yes + dhcp4: no + addresses: [192.168.1.101/24] + gateway4: 192.168.1.1 + nameservers: + addresses: [114.114.114.114,8.8.8.8] + enaphyt4i1: + optional: yes + dhcp4: no + addresses: [192.168.1.102/24] + gateway4: 192.168.1.1 + nameservers: + addresses: [114.114.114.114,8.8.8.8] +``` + +解决方法: + +们为每个接口指定了唯一的路由表和默认路由。请注意 routes 和 routing-policy 部分。routes 部分指定了默认路由表和默认路由。routing-policy 部分指定了从哪个子网访问路由表。在这个例子中,我们使用了不同的路由表编号(101 和 102),并且为每个接口指定了唯一的路由策略。 + +```yaml +# This is the network config written by 'subiquity' +network: + version: 2 + renderer: networkd + ethernets: + enaphyt4i0: + optional: yes + dhcp4: no + addresses: [192.168.1.101/24] + routes: + - to: 0.0.0.0/0 + via: 192.168.1.0 + table: 101 + routing-policy: + - from: 192.168.1.0/24 + table: 101 + nameservers: + addresses: [114.114.114.114,8.8.8.8] + enaphyt4i1: + optional: yes + dhcp4: no + addresses: [192.168.1.102/24] + routes: + - to: 0.0.0.0/0 + via: 192.168.1.0 + table: 102 + routing-policy: + - from: 192.168.1.0/24 + table: 102 + nameservers: + addresses: [114.114.114.114,8.8.8.8] +``` + +但是这样配置之后,没办法访问外网,ping不通外网。 + +### 无法启动 + +其中一台机器安装了4.19的内核之后启动不了: + +其中一台启动之后出现企鹅排队的画面,等待一段时间可以进入系统(显示屏变成白色,等待一段时间之后可以看到登录界面)。 + +**但是**,另外一台机器启动之后现企鹅排队的画面,等待一段时间之后显示屏变成白色,但是无法进入系统。(手册显示可能会卡住数十秒不动;但是我的这台机器可能卡住很久都不动)。在grub菜单中选择”Advance option..." , -> "ubuntu, with linux 4.19.6-phy",还是"ubuntu, with linux 4.19.0",都无法进入系统。 + +是installing virtual address map出错吗? + +### ssh连接不上 + +假设工控机两个网口地址为192.168.1.100和192.168.1.103,使用远程机器ssh连接有时候连接不上(ssh test01@192.168.1.103),但是ping 192.168.1.103可以ping通。 + +显示信息如下: + +``` +kex_exchange_identification: read: Connection reset by peer +Connection reset by 192.168.1.103 port 22 +``` + +可能原因:服务器禁止了我这个IP进行连接,所以把这个IP删除就可以。 +解决办法如下: + +``` +vi /etc/hosts.deny +``` + +如果这个文件中存在你的电脑IP的话,把那个IP从这个文件中删除就可以,然后安ESC退出,:q进行保存即可。 +hosts.deny文件中没有内容。 + +解决方法二: + +``` +ssh-keygen -R XXX.XXX.XXX.XXX // XXX.XXX.XXX.XXX 为要登陆主机的IP地址 +``` + +还是不行,显示 + +``` +cannot start /root/.ssh/known_hosts: No such file or directory +``` + +解决方法三: + +https://www.cnblogs.com/lfri/p/15257145.html + +``` +vim /etc/hosts.allow # 添加ssh:ALL +``` + +重启ssh服务 + +``` +service sshd restart +``` + +可能原因二: + +https://help.aliyun.com/document_detail/41485.html + +可能原因三: +端口没有对外开放 + +``` +sudo ufw allow 22 +``` + +或者 + +``` +/sbin/iptables -I INPUT -p tcp --dport 22 -j ACCEPT +``` + +可能解决方法: + +``` +ssh -vvv test01@192.168.1.103 +``` + +这将在终端中显示 SSH 连接的详细日志信息,包括连接过程中发送和接收的所有消息。根据日志信息,你可以更好地了解连接失败的原因,并采取相应的措施来解决问题。 + +使用 telnet 命令测试目标主机的 SSH 端口是否可用: + +``` +telnet 192.168.1.103 22 +``` + +如果你使用了 SSH 密钥对进行身份验证,请确保你的公钥已经添加到目标主机的授权文件中。 + +``` +ssh-copy-id -i ~/.ssh/id_rsa.pub test01@192.168.1.103 +``` + +### 两个网口只能ssh其中一个 + +### 编译好内核之后apt install任何包裹都报错 + +``` +libnl-route-3-200 : Depends: libnl-3-200 (= 3.4.0-1) but 3.4.0-1ubuntu0.1 is to be installed +``` + +固定了IP地址之后,无法访问外网,但是可以访问内网。 + +例如,安装docker失败:(包括apt upgrade) +libnl-route-3-200? + +解决方法: 先安装docker再配置内核? + +``` +Reading package lists... Done +Building dependency tree +Reading state information... Done +You might want to run 'apt --fix-broken install' to correct these. +The following packages have unmet dependencies: + docker.io : Depends: containerd (>= 1.2.6-0ubuntu1~) but it is not going to be installed + Recommends: pigz but it is not going to be installed + Recommends: ubuntu-fan but it is not going to be installed + libnl-route-3-200 : Depends: libnl-3-200 (= 3.4.0-1) but 3.4.0-1ubuntu0.1 is to be installed +E: Unmet dependencies. Try 'apt --fix-broken install' with no packages (or specify a solution). +``` + +例如,wpasupplicant安装失败,报错提示如下: + +``` +libnl-route-3-200: arm64is not configured yet +``` + +解决方法: + +下载[libnl-route-3-200_3.4.0-1ubuntu0.1_arm64.deb](https://ubuntu.pkgs.org/20.04/ubuntu-updates-main-arm64/libnl-route-3-200_3.4.0-1ubuntu0.1_arm64.deb.html) + +还有这个: + +[libnl-3-200_3.4.0-1ubuntu0.1_arm64.deb](https://ubuntu.pkgs.org/20.04/ubuntu-updates-main-arm64/libnl-3-200_3.4.0-1ubuntu0.1_arm64.deb.html) + +然后安装: + +```shell +dpkg -i libnl-route-3-200_3.4.0-1ubuntu0.1_arm64.deb +``` + +### 网口不能自动启动 + +每次都需要dhclient来启动网口。 + +### 卸载docker + +``` +apt remove docker.io +``` + +## 网络配置好之后过一段时间无法ping通外网 + +``` +dhclient -r +dhclient +``` diff --git "a/_posts/2023-06-23-\346\250\241\347\263\212\346\216\247\345\210\266\347\232\204\347\220\206\350\256\272\345\237\272\347\241\200\345\222\214\346\250\241\347\263\212\351\200\273\350\276\221\346\216\247\345\210\266.md" "b/_posts/2023-06-23-\346\250\241\347\263\212\346\216\247\345\210\266\347\232\204\347\220\206\350\256\272\345\237\272\347\241\200\345\222\214\346\250\241\347\263\212\351\200\273\350\276\221\346\216\247\345\210\266.md" new file mode 100644 index 0000000000..765758b0c8 --- /dev/null +++ "b/_posts/2023-06-23-\346\250\241\347\263\212\346\216\247\345\210\266\347\232\204\347\220\206\350\256\272\345\237\272\347\241\200\345\222\214\346\250\241\347\263\212\351\200\273\350\276\221\346\216\247\345\210\266.md" @@ -0,0 +1,13 @@ +--- +layout: post +title: 模糊控制的理论基础和模糊逻辑控制 +date: 2023-06-23 +description: "模糊控制的理论基础和模糊逻辑控制" +tag: 控制理论 +--- + +## 1. 模糊控制的理论基础 + +### 1.1 模糊集合 + + diff --git "a/_posts/2023-06-27-\344\275\277\347\224\250pytorch\351\242\204\346\265\213\351\243\216\351\200\237.md" "b/_posts/2023-06-27-\344\275\277\347\224\250pytorch\351\242\204\346\265\213\351\243\216\351\200\237.md" new file mode 100644 index 0000000000..bb8929b737 --- /dev/null +++ "b/_posts/2023-06-27-\344\275\277\347\224\250pytorch\351\242\204\346\265\213\351\243\216\351\200\237.md" @@ -0,0 +1,85 @@ +--- +layout: post +title: 使用pytorch预测风速 +date: 2023-06-27 +description: "使用pytorch预测风速" +tag: 机器学习 +--- + +# 循环神经网络简介 + + +循环神经网络(Recurrent Neural Network, RNN) + +循环神经网络的主要用途是处理和预测序列数据,传统的神经网络模型中,我们假设所有的输入都是相互独立的,从输入层到隐藏层再到输出层,层与层之间是全连接的,每层之间的节点是无连接的。但是在实际生活中,很多数据都是有前后关系的,比如自然语言,我们在看一段话的时候,往往是要根据前面的话才能理解后面的话的。循环神经网络就是为了解决这种时序相关性的问题而提出的。 + +RNN隐藏层相互连接,也即是一个序列的输出与前面的输出也有关,也拥有捕获已计算节点信息的记忆能力。RNN的隐藏层可以看成是一个内部的循环,信息可以在这个循环中传递,这也是RNN名字的由来。 + +循环神经网络展开如下: + +![20230627161712](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230627161712.png) + +其中,x 是输入层的值,s 是隐藏层的值,U 是输入层到隐藏层的权重矩阵。 +o 表示输出层的值,V 是隐藏层到输出层的权重矩阵。循环神经网络的隐藏 +层的值s 不仅仅取决于当前这次的输入x,还取决于上一次隐藏层的值s。权重矩阵W 就是隐藏层上一次的值作为这一次的输入的权重。RNN 每一层的权重 W 都是共享的。总体 Loss 由每一步的 Loss 组合而成。 + +在这张图片中,$x_t$表示t=1,2,...,T时刻的输入,$s_t$是隐藏层第t步的状态,由当前步骤的输入和先前隐藏状态共同计算得到求得: + +$$ +S_t = f(Ux_t + Ws_{t-1}) +$$ + +$O_t$是t时刻的输出。 + +循环神经网络中由于输入时叠加了之前的信号,所以反向传导时不同于传统的神经网络,需要对每个时间步的参数进行更新,这就是所谓的BPTT算法(Back Propagation Through Time-BPTT)。 + +![20230627164345](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230627164345.png) + +前向传播过程中: + +$$ +\begin{array}{ll} +s_1=f\left(U x_1+W s_0+b\right) & o_1=g(V s_1+c) \\ +s_2=f\left(U x_2+W s_1+b\right) & o_2=g(V s_2+c) \\ +s_3=f\left(U x_3+W s_2+b\right) & o_3=g(V s_3+c) +\end{array} +$$ + +反向传播过程中,对于每个时间步,都需要计算相应的梯度,然后将梯度进行累加,最后对参数进行更新。 + +我们这里计算t=3时刻的U、V、W的求损失函数L_3的偏导: + +$$ +\begin{aligned} +\frac{\partial L_3}{\partial U} & =\frac{\partial L_3}{\partial O_3} \frac{\partial O_3}{\partial S_3} \frac{\partial S_3}{\partial U}+\frac{\partial L_3}{\partial O_3} \frac{\partial O_3}{\partial S_3} \frac{\partial S_3}{\partial S_2} \frac{\partial S_2}{\partial U}+\frac{\partial L_3}{\partial O_3} \frac{\partial O_3}{\partial S_3} \frac{\partial S_3}{\partial S_2} \frac{\partial S_2}{\partial S_1} \frac{\partial S_1}{\partial U} \\ +\frac{\partial L_3}{\partial V} & =\frac{\partial L_3}{\partial S_3} \frac{\partial S_3}{\partial V} \\ +\frac{\partial L_3}{\partial W} & =\frac{\partial L_3}{\partial O_3} \frac{\partial O_3}{\partial S_3} \frac{\partial S_3}{\partial W}+\frac{\partial L_3}{\partial O_3} \frac{\partial O_3}{\partial S_3} \frac{\partial S_3}{\partial S_2} \frac{\partial S_2}{\partial W}+\frac{\partial L_3}{\partial O_3} \frac{\partial O_3}{\partial S_3} \frac{\partial S_3}{\partial S_2} \frac{\partial S_2}{\partial S_1} \frac{\partial S_1}{\partial W} +\end{aligned} +$$ + +若t足够大,则式子中的加数就很多,任意时刻t时对U、W求偏导,以W为例: + +$$ +\frac{\partial L_t}{\partial W}=\sum_{k=0}^t \frac{\partial L_t}{\partial O_t} \frac{\partial O_t}{\partial S_t}\left(\prod_{j=k+1}^t \frac{\partial S_j}{\partial S_{j-1}}\right) \frac{\partial S_j}{\partial W} +$$ + +可以看出,当时间序列足够长,t足够大时,梯度会指数级的衰减,这就是著名的梯度消失问题,伴随着梯度消失的是RNN的记忆能力的衰减,这使得RNN难以捕捉到时间序列中时间跨度较大的依赖关系。 + +LSTM网络的出现就是为了解决这个问题。 + +# GRU + +GRU是LSTM的变体,它将LSTM中的输入门和遗忘门合并成了一个更新门,同时合并了细胞状态和隐藏状态,使得模型更加简单。每个前面状态对当前的影响都进行了距离加权,距离越远,权重越小。 + +GRU网络结构如下: + +![20230627175123](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230627175123.png) + +参考文献: + +[史上最详细循环神经网络讲解(RNN/LSTM/GRU) +](https://zhuanlan.zhihu.com/p/123211148) + +[深度学习中的循环神经网络GRU详解](https://blog.csdn.net/sinat_36618660/article/details/100798425) + +[GRU实现时间序列预测(PyTorch版)](https://blog.csdn.net/m0_47256162/article/details/128595011) \ No newline at end of file diff --git "a/_posts/2023-07-26-Django\351\241\271\347\233\256\345\256\271\345\231\250\345\214\226\345\256\236\346\210\230.md" "b/_posts/2023-07-26-Django\351\241\271\347\233\256\345\256\271\345\231\250\345\214\226\345\256\236\346\210\230.md" new file mode 100644 index 0000000000..8f5911602d --- /dev/null +++ "b/_posts/2023-07-26-Django\351\241\271\347\233\256\345\256\271\345\231\250\345\214\226\345\256\236\346\210\230.md" @@ -0,0 +1,314 @@ +--- +layout: post +title: Django项目容器化实战 +date: 2023-07-26 +description: "Django项目容器化实战" +tag: Docker +--- +## 项目地址 + +[Django博客](https://gitee.com/agagin/python-demo) + +## 项目结构 + +```txt +├─blog +│ ├─migrations +│ └─templates +├─log +├─myblog +├─resources +│ ├─myblog +│ └─mysql +└─static + └─admin + ├─css + │ └─vendor + │ └─select2 + ├─fonts + ├─img + │ └─gis + └─js + ├─admin + └─vendor + ├─jquery + ├─select2 + │ └─i18n + └─xregexp +``` + +## Dockerfile + +通常把Dockerfile放在业务的根目录下:这 + +```dockerfile +# Base Image基础镜像 centos:centos7.5.1804;:之后的centos7.5.1804表示标签 +FROM centos:centos7.5.1804 + +# ENV设置环境变量,这里设置了两个环境变量; LANG:语言;LC_ALL:所有区域 +ENV LANG en_US.UTF-8 +ENV LC_ALL en_US.UTF-8 + +# RUN执行命令,这里是在容器中curl下载了Centos-7.repo文件,然后安装了nginx, -s表示静默模式,不显示进度条,-o表示输出到指定文件; rpm -Uvh:安装rpm包,-U:升级已经安装的包,-v:显示详细信息,-h:显示进度条; RPM包是Red Hat Package Manager的缩写,是Red Hat Linux发行版的软件安装包格式,类似于Windows中的exe安装包 +RUN curl -so /etc/yum.repos.d/Centos-7.repo http://mirrors.aliyun.com/repo/Centos-7.repo && rpm -Uvh https://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm +RUN yum install -y python3.6 python3-devel gcc pcre-devel zlib-devel make net-tools nginx + +# 设置工作目录 +WORKDIR /opt/myblog + +# 将当前目录下的所有文件拷贝到容器中的/opt/myblog目录下; +COPY ./* /opt/myblog + +# 拷贝nginx配置文件 +COPY myblog.conf /etc/nginx +# 安装依赖的插件 +RUN pip3 install -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com -r requirements.txt + +RUN chmod +x run.sh && rm -rf ~/.cache/pip + +# 映射端口 +EXPOSE 8002 + +# 容器启动时执行命令 +CMD ["./run.sh"] +``` + +## nginx配置文件 + +在根目录的myblog.conf是nginx配置文件信息: + +worker_process表示 + +```txt + +#user nobody; +worker_processes 4; + +#error_log logs/error.log; +#error_log logs/error.log notice; +#error_log logs/error.log info; + +#pid logs/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include mime.types; + default_type application/octet-stream; + + #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + # '$status $body_bytes_sent "$http_referer" ' + # '"$http_user_agent" "$http_x_forwarded_for"'; + + #access_log logs/access.log main; + + sendfile on; + #tcp_nopush on; + + #keepalive_timeout 0; + keepalive_timeout 65; + + #gzip on; + + + server { + listen 8002; + server_name 127.0.0.1; + charset utf-8; + access_log /opt/myblog/log/nginx_access.log; + error_log /opt/myblog/log/nginx_error.log; + client_max_body_size 75M; + + + location /static { + alias /opt/myblog/static; + } + location /media { + alias /opt/myblog/media; + } + location / { + root /opt/myblog; + include /etc/nginx/uwsgi_params; + uwsgi_pass 127.0.0.1:8000; + } + } + + +} +``` + +## run.sh和requirements.txt + +```sh +#!/bin/bash +uwsgi --ini ./uwsgi.ini +nginx -c /etc/nginx/myblog.conf -g 'daemon off;' +``` + +```txt +PyMySQL==0.9.3 +Django==2.1.8 +uwsgi==2.0.18 +``` + +## 删除镜像和容器 + +有的时候我们需要删除镜像或者容器,可以使用如下命令: + +```sh +docker ps -a +docker rm -f 容器id +docker images +docker rmi 镜像id +``` + +## 执行构建 + +这一步是根据Dockerfile构建镜像,-t表示指定镜像名称,-f表示指定Dockerfile文件; + +```sh +docker build . -t myblog:v1 -f Dockerfile +``` + +## 运行mysql + +mysql也是中间件,需要单独运行容器: +-v表示挂载目录,-e表示环境变量,-d表示后台运行; + +这一步骤可能存在问题,没有权限无法挂载目录,可以使用--privileged=true参数,表示给容器赋予所有的权限; + +```sh +docker run --privileged=true -p 3306:3306 --name mysql -v /opt/mysql:/var/lib/mysql -e MYSQL_DATABASE=myblog -e MYSQL_PASSWORD=123456 mysql:5.7 + +# 进入容器,查看数据库 +docker exec -it mysql bash +#/ mysql -p123456 +#/ show databases; +``` + +还是没有启动容器,报错如下: + +```txt +2023-07-26 06:04:42+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified + You need to specify one of the following as an environment variable: + - MYSQL_ROOT_PASSWORD + - MYSQL_ALLOW_EMPTY_PASSWORD + - MYSQL_RANDOM_ROOT_PASSWORD +``` + +修改如下: + +```sh +docker run --privileged=true -p 3306:3306 --name mysql -v /opt/mysql:/var/lib/mysql -e MYSQL_DATABASE=myblog -e MYSQL_PASSWORD=123456 -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql:5.7 +``` + +这个项目中,database的设置在myblog/settings.py中: + +os.environ.get('MYSQL_DATABASE', 'root')表示获取环境变量中的MYSQL_DATABASE,如果没有就使用root; + +下面的其他三个语句都是一样的,说明启动这个数据库只需要设置四个环境变量,分别是:"MYSQL_DATABASE"、"MYSQL_PASSWD"、"MYSQL_HOST"和"MYSQL_PORT"。 + + + +![20230726134806](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230726134806.png) + +容器启动成功,但是无法访问: + +```shell +docker exec -ti mysql bash +bash-4.2# mysql -p123456 +mysql: [Warning] Using a password on the command line interface can be insecure. +ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES) +bash-4.2# mysql -uroot +ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO) +bash-4.2# mysql -u root +ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO) +bash-4.2# mysql -u root -p +Enter password: +ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO) +``` + +解决办法:修改my.in/my.cnf配置文件 + +进入mysql安装目录(linux环境:/etc/my.cnf,也可以通过mysql --help查看) + +编辑my.ini,这里要注意,如果你没有my.ini文件,就在Mysql安装目录下新建一个 + +在[mysqld]下添加skip-grant-tables,保存即可!(意思是跳过验证) + +然后重启服务即可 + +```sh +docker restart mysql对应的容器id +``` +查看mysql: + +```sh +docker exec -ti mysql bash +bash-4.2 # mysql -uroot +mysql> use myblog; +mysql> show tables; +``` +显示结果如下: + +```txt +#/ mysql -p123456 +#/ show databases; +``` + +![20230726145355](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230726145355.png) + +### 其他可能错误原因 + +[参考链接](https://readdevdocs.com/blog/tech/%E8%A7%A3%E5%86%B3Mysql%E9%94%99%E8%AF%AF%EF%BC%9AERROR%201045%20(28000).html#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88) + + +还是不行: + +如果确认自己的明文密码是正确的,原因有可能是mysql8.0使用的默认加密插件是 caching_sha2_password 而非5.6的 mysql_native_password,当你在控制台使用明文密码登录的时候走的是mysql_native_password加密,而数据库记录的是caching_sha2_password加密后的密码,匹配自然不正确。这个概论很小 + + +## 启动Django应用 + +```sh +# 启动容器 +docker run -d -p 8002:8002 --name myblog -e MYSQL_HOST=172.21.56.67 -e MYSQL_USER=root -e MYSQL_PASSWD=123456 myblog:v1 +``` + +启动之后,可以通过docker ps查看容器是否启动成功,如果启动成功,可以通过浏览器访问:172.21.56.67:8002,发现无法访问,查看报错信息: + +如果docker ps没有显示容器,且使用docker ps -a发现容器已经exit,可以使用如下命令查看容器日志: + +```sh +docker logs 容器id +``` + +错误原因如下: + +```txt +exec ./run.sh: no such file or directory +``` + +说明并没有进入需要的工作目录!!! + +原因是因为是个空表,需要先创建表格!! + +```sh +docker exec -it myblog bash # 进入myblog容器 +``` + +在容器中执行如下命令: + +```sh +pwd # 确定单前目录为/opt/myblog +python3 manage.py makemigrations # 生成迁移文件,这一步会在migrations目录下生成0001_initial.py文件 +python3 manage.py migrate # 执行迁移文件,这一步会在数据库中生成表格 +python3 manage.py createsuperuser # 创建超级用户 +``` + +这个时候,就可以愉快的访问172.51.67:8080/admin了! + diff --git "a/_posts/2023-07-28-Docker\344\270\255\350\277\220\350\241\214\345\222\214\351\203\250\347\275\262Hugo\345\215\232\345\256\242.md" "b/_posts/2023-07-28-Docker\344\270\255\350\277\220\350\241\214\345\222\214\351\203\250\347\275\262Hugo\345\215\232\345\256\242.md" new file mode 100644 index 0000000000..64255a1bb5 --- /dev/null +++ "b/_posts/2023-07-28-Docker\344\270\255\350\277\220\350\241\214\345\222\214\351\203\250\347\275\262Hugo\345\215\232\345\256\242.md" @@ -0,0 +1,70 @@ +--- +layout: post +title: Docker中运行和部署Hugo博客 +date: 2023-07-28 +description: "Docker中运行和部署Hugo博客" +tag: Docker +--- +# 创建Dockerfile + +```shell +#使用基础镜像 +FROM debian:12.0-slim + +# 安装hugo +RUN apt-get update \ + && apt-get install -y hugo \ + && rm -rf /var/lib/apt/lists/* + +# 暴露1313端口 +EXPOSE 1313/tcp + +# entrypoint防止docker 容器运行后退出 +ENTRYPOINT ["tail", "-f", "/dev/null"] +``` + +## 构建镜像 + +在Dockerfile对应的文件夹⬇️执行 : + +```shell +docker build -t hugotools:1.0 . +``` + +执行完将通过我们的Dockerfile来构建一个hugotools 1.0版本的镜像! + +## 运行容器 + +```shell +docker run --name hugotool --rm -p 127.0.0.1:1313:1313 -v /home/hugo/hugo-blog-data:/app hugotools:1.0 +``` + +/home/hugo/hugo-blog-data是我本地的hugo博客文件夹,/app是容器内的hugo博客文件夹,这样就可以将本地的hugo博客文件夹挂载到容器内,这样就可以在本地编辑博客,然后在容器内运行hugo server来预览博客了。 + +## 进入容器 + +```shell +docker ps +docker exex -it /bin/bash +``` + +## 运行hugo server + +现在已经进入了容器内部。 + +```bash +cd /app +hugo new site myblog +cd myblog +hugo new posts/my-first-post.md +``` + +这里我们创建了一个名为myblog的hugo博客,然后创建了第一篇博客。 + +下载[主题模板](https://themes.gohugo.io/)到myblog/themes/目录下,然后修改config.toml文件,将主题改为我们下载的主题。 + +```bash +hugo server --theme=/app/myblog/themes/hugo-theme-learn --bind= +``` + +hugo server 命令执行后, hugo server 将运行起来,并且会监听1313端口,我们可以通过浏览器访问本地址发布的博客网站了。 diff --git "a/_posts/2023-08-04-\345\234\250Centos\344\270\255\345\256\211\350\243\205\345\222\214\351\203\250\347\275\262K8S\347\216\257\345\242\203.md" "b/_posts/2023-08-04-\345\234\250Centos\344\270\255\345\256\211\350\243\205\345\222\214\351\203\250\347\275\262K8S\347\216\257\345\242\203.md" new file mode 100644 index 0000000000..43ffd796ec --- /dev/null +++ "b/_posts/2023-08-04-\345\234\250Centos\344\270\255\345\256\211\350\243\205\345\222\214\351\203\250\347\275\262K8S\347\216\257\345\242\203.md" @@ -0,0 +1,216 @@ +--- +layout: post +title: 在Centos中安装和部署K8S环境 +date: 2023-08-04 +description: "在Centos中安装和部署K8S环境" +tag: Docker +--- +## 基础概念 + +kubernetes,简称 K8s,是用8代替8个字符“ubernete”而成的缩写。是一个开源的,用于管理云平台中多个主机上的容器化的应用,Kubernetes 的目标是让部署容器化的应用简单并且高效(powerful),Kubernetes提供了应用部署,规划,更新,维护的一种机制。 + +kubeadm是官方社区推出的一个用于快速部署kubernetes集群的工具,这个工具能通过两条指令完成一个kubernetes集群的部署。 + +```bash +kubeadm init +kubeadm join # 这个再init之后执行 +``` + +## 环境准备 + +### 主机名配置 + +主节点: + +```shell +hostnamectl set-hostname centos-master1 +``` + +从节点: + +```shell +hostnamectl set-hostname centos-node1 +set-hostname centos-node2 +``` + +### 配置主机名解析 + +修改配置文件,vim /etc/hosts + +```bash +127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 +::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 +192.168.160.129 centos-master1 +192.168.160.130 centos-node1 +192.168.160.131 centos-node2 +``` + +### 关闭防火墙 + +在每台机器上都运行!!! + +```bash +systemctl disable firewalld +systemctl stop firewalld +firewall-cmd --state +``` + +### 关闭selinux + +在每台机器上都运行!!! + +```bash +systemctl disable firewalld +systemctl stop firewalld +firewall-cmd --state +``` + +```bash + +setenforce 0 +sed -i 's/enforcing/disabled/' /etc/selinux/config +``` + +### 配置网桥模块 + +查看是否加载 + +```bash +lsmod | grep br_netfilter +``` + +## 安装docker + +所有主机执行!!! + +Kubernetes默认CRI(容器运行时)为Docker,因此先安装Docker。 + +这次安装地docker版本为18.06.1版本: + +```bash +wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo # 下载docker源 +yum install -y docker-ce-18.06.1.ce-3.el7 # 安装docker +ystemctl enable docker && systemctl start docker +docker --version +``` + +### 替换镜像仓库以及修改cgroupdriver + +```bash +sudo tee /etc/docker/daemon.json <<-'EOF' +{ + "registry-mirrors": ["https://p4e9di9t.mirror.aliyuncs.com"], + "exec-opts": ["native.cgroupdriver=systemd"] +} +EOF +``` + +### 设置k8s的YUM源 + +```bash +cat > /etc/yum.repos.d/kubernetes.repo << EOF +[kubernetes] +name=Kubernetes +baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64 +enabled=1 +gpgcheck=0 +repo_gpgcheck=0 +gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg +EOF +``` + +## 安装kubeadm,kubelet和kubectl + +安装指定版本的1.18,避免兼容性问题 + +```bash +yum install -y kubelet-1.18.0 kubeadm-1.18.0 kubectl-1.18.0 +# 开机自启动 +systemctl enable kubelet +``` + +## 主节点初始化 + +```bash +kubeadm init --apiserver-advertise-address=192.168.160.129 --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.18.0 --service-cidr=10.1.0.0/16 --pod-network-cidr=10.244.0.0/16 +``` + +### 复制配置文件,并授权 + +在master节点上,根据提示执行: + +```bash +# To start using your cluster, you need to run the following as a regular user: + +mkdir -p $HOME/.kube +sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config +sudo chown $(id -u):$(id -g) $HOME/.kube/config +``` + +### 安装网络插件 + +```bash +curl -O https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml +kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml +``` + +### master生成token + +```bash +kubeadm token create --print-join-command +``` + +结果如下: + +```txt +[root@centos-master ~]# kubeadm token create --print-join-command +W0801 20:43:27.871329 8583 configset.go:202] WARNING: kubeadm cannot validate component configs for API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io] +kubeadm join 192.168.160.129:6443 --token a57eqj.em1acygcl6hyol80 --discovery-token-ca-cert-hash sha256:ce0d4655dff4bca6e1b21d1cf79ca1c9d51396211c7d1c30088b39c893c183ef +``` + +## 连接其他节点 + +在node节点上执行: + +```bash +kubeadm join 192.168.160.129:6443 --token a57eqj.em1acygcl6hyol80 --discovery-token-ca-cert-hash sha256:ce0d4655dff4bca6e1b21d1cf79ca1c9d51396211c7d1c30088b39c893c183ef +``` + +然后,主节点查看节点状态: + +```bash +kubectl get nodes +``` + +![20230804180146](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230804180146.png) + +### 报错提示 + + +这个是由于还没有挂载上node节点,等待一会儿就好了。 + +```txt +[root@centos-node2 ~]# kubeadm join 192.168.160.129:6443 --token a57eqj.em1acygcl6hyol80 --discovery-token-ca-cert-hash sha256:ce0d4655dff4bca6e1b21d1cf79ca1c9d51396211c7d1c30088b39c893c183ef +W0801 20:50:11.379651 6789 join.go:346] [preflight] WARNING: JoinControlPane.controlPlane settings will be ignored when control-plane flag is not set. +[preflight] Running pre-flight checks + [WARNING Hostname]: hostname "centos-node2" could not be reached + [WARNING Hostname]: hostname "centos-node2": lookup centos-node2 on 192.168.160.2:53: no such host +[preflight] Reading configuration from the cluster... +[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml' +[kubelet-start] Downloading configuration for the kubelet from the "kubelet-config-1.18" ConfigMap in the kube-system namespace +[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml" +[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env" +[kubelet-start] Starting the kubelet +[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap... + +This node has joined the cluster: +* Certificate signing request was sent to apiserver and a response was received. +* The Kubelet was informed of the new secure connection details. + +Run 'kubectl get nodes' on the control-plane to see this node join the cluster. +``` + +## 参考链接 + +- [使用kubeadm快速部署一个K8s集群、并安装KubeSphere](https://juejin.cn/post/7252586674580275237) +- [OpenEuler部署K8S集群](https://blog.csdn.net/qq_44872331/article/details/131960271?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22131960271%22%2C%22source%22%3A%22qq_44872331%22%7D) diff --git "a/_posts/2023-08-08-Linux\347\243\201\347\233\230\347\256\241\347\220\206\345\217\257\350\247\206\345\214\226\345\267\245\345\205\267.md" "b/_posts/2023-08-08-Linux\347\243\201\347\233\230\347\256\241\347\220\206\345\217\257\350\247\206\345\214\226\345\267\245\345\205\267.md" new file mode 100644 index 0000000000..f71510f825 --- /dev/null +++ "b/_posts/2023-08-08-Linux\347\243\201\347\233\230\347\256\241\347\220\206\345\217\257\350\247\206\345\214\226\345\267\245\345\205\267.md" @@ -0,0 +1,35 @@ +--- +layout: post +title: Linux磁盘管理可视化工具 +date: 2023-08-08 +description: "Linux磁盘管理可视化工具" +tag: Linux +--- + +## 虚拟机扩容 + + 在将需要扩容的虚拟机关机后,点击VMware的菜单栏中的虚拟机,选择虚拟机设置,点击硬盘,然后选择扩展(虚拟机关机后,才可操作),就可以修改虚拟机的磁盘大小了,我将其大小从50GB扩展到了100GB,用了大约几分钟。。 + +## 查看磁盘信息 + +```bash +fdisk -l +``` + +## Ubuntu扩容 + +采用可视化分区工具gparted进行重新分区: + +```bash +sudo apt-get install gparted +# 启动gparted图形化界面 +sudo gparted +``` + + +选择要扩容的分区,右键选择Resize/Move,将分区扩大到最大,点击Resize/Move,然后点击Edit->Apply All Operations,等待操作完成即可。 + +这张图显示的已经是扩容之后的了: + + +![20230808151649](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230808151649.png) \ No newline at end of file diff --git "a/_posts/2023-08-09-\345\211\215\345\220\216\347\253\257\345\210\206\347\246\273\351\241\271\347\233\256\347\256\200\345\215\225\345\256\236\350\267\265.md" "b/_posts/2023-08-09-\345\211\215\345\220\216\347\253\257\345\210\206\347\246\273\351\241\271\347\233\256\347\256\200\345\215\225\345\256\236\350\267\265.md" new file mode 100644 index 0000000000..270f29a6e1 --- /dev/null +++ "b/_posts/2023-08-09-\345\211\215\345\220\216\347\253\257\345\210\206\347\246\273\351\241\271\347\233\256\347\256\200\345\215\225\345\256\236\350\267\265.md" @@ -0,0 +1,259 @@ +--- +layout: post +title: 前后端分离项目简单实践 +date: 2023-08-09 +description: "前后端分离项目简单实践" +tag: 后端 +--- +## 开发环境搭建 + +### 前端开发环境准备 + +* 需要安装Node.js环境 +* 使用npm初始化项目:npm init +* 安装Vue框架:npm install vue +* 安装webpack等构建工具:npm install webpack webpack-cli -D + +```bash +npm install -g vue-cli +npm install webpack -g +``` + +## 创建前后端分离项目 + +创建一个名为myapp的文件夹: + +```bash +mkdir myapp +cd myapp +``` + +### 创建后端项目 + +```bash +mkdir backend +cd backend +``` + +在backend文件夹中创建一个名为app.py的文件,并将以下代码复制到该文件中。 + +```python +from flask import Flask, request, jsonify + +app = Flask(__name__, static_folder='../frontend/dist', static_url_path='') + +# 路由:返回访客的名字 +@app.route('/api/name') +def get_name(): + name = request.args.get('name', 'Guest') + return jsonify({'name': name}) + +# 路由:返回Python代码示例,以实现访客的想法 +@app.route('/api/idea', methods=['POST']) +def post_idea(): + idea = request.json['idea'] + # 在此处编写代码示例,以实现访客的想法 + code = f'print("Your idea was: {idea}")' + return jsonify({'code': code}) + +if __name__ == '__main__': + app.run() +``` + +在backend文件夹中创建一个名为requirements.txt的文件,并将以下代码复制到该文件中。 + +```txt +flask +``` + +### 创建前端程序 + +```bash +cd .. # 返回myapp文件夹 +mkdir frontend +cd frontend +``` + +运行以下命令来创建一个新的Vue.js项目: + +``` +vue create . +``` + +在创建项目时,可以选择使用默认设置或手动配置设置。 + +在frontend文件夹中打开src/App.vue文件,并将以下代码复制到该文件中。 + +```vue + + + +``` + +在frontend文件夹中打开vue.config.js文件,并将以下代码复制到该文件中: + +```js +module.exports = { + outputDir: '../backend/static' +}; +``` + +此代码将生成的静态文件输出到backend/static文件夹中! + +### 安装依赖 + +返回myapp文件夹,并运行以下命令来安装必要的Python和JavaScript依赖项! + +```bash +cd .. # 返回myapp文件夹 +pip install -r backend/requirements.txt # 安装flask +cd frontend +npm install axios # 安装axios,这个是用来发送http请求的 +``` + +## 启动项目 + +### 启动后端 + +```bash +cd .. # 返回myapp文件夹 +python backend/app.py +``` + +### 启动前端 + +```bash +cd frontend +npm run serve +``` + +### 访问项目 + +此时,前后端项目已经启动!可以在Web浏览器中访问http://localhost:5000来查看应用程序。 + +### 目录树结构 + +``` +myapp/ +├── backend/ +│ ├── app.py +│ └── static/ +│ ├── css/ +│ ├── img/ +│ └── js/ +└── frontend/ + ├── public/ + ├── src/ + ├── vue.config.js + ├── package.json + └── ... +``` + +其他: + +``` +project +├── backend +| ├── app.py (Flask后端主文件) +| └── requirements.txt (后端依赖) +├── frontend +| ├── public +| | └── index.html +| ├── src +| | ├── main.js (入口文件) +| | ├── App.vue (主组件) +| | └── components (其他组件) +| ├── package.json (前端依赖) +| └── webpack.config.js (webpack配置) +└── nginx + └── default.conf (nginx配置文件) +``` + +在此目录树结构中,backend文件夹包含后端代码和静态文件,frontend文件夹包含前端代码和依赖项。请注意,frontend/public文件夹中的文件将直接复制到生成的静态文件中,而frontend/src文件夹中的文件将被编译和打包为JavaScript文件。 + +## 项目部署 + +部署可以按以下流程进行: +1.在服务器上准备Python、Nodejs等运行环境 +2. 配置nginx,以反向代理方式处理前后端请求 +3. 后端: + - 上传Flask应用代码 + - 安装依赖 pip install -r requirements.txt + - 启动Flask应用 +4. 前端: + - 构建生成静态文件 npm run build + - 上传dist目录中的静态文件到服务器 +5. nginx配置: + - 配置路由规则,例如:/api转发到flask端口/转发到静态文件目录 +6. 启动nginx +7.测试部署是否成功 + +## 注意事项 + +- 前端: + +1)运行后的项目,其他终端能通过 http://localhost:8080 访问查看页面吗? +A:npm run dev 命令只用做本地调试,共开发者预览页面,同部署到服务器供其他终端访问是不同的,如果要提供给其他浏览器或终端使用,则需要部署到具体的服务器才行。 +2)如何将 Vue 项目部署到服务器上呢? +执行命令 npm run build 生成 dist 文件夹,然后将该目录下的文件部署到服务器(服务器如python,tomcat,nginx)。 +cd 到 dist 目录下,这里使用最简单的 python 搭建服务器,运行命令:python -m http.server,将 dist 下的文件部署到 Python 自带的服务器中; +访问 http://YourIpAddress:8000/ 可以直接在其他终端打开对应的页面(python搭建的服务器再8000端口监听)。 +3)如何通过 Vue 请求服务器接口并获取数据呢? + +- 后端: + +1)如何为外部提供接口?(base_url = http://ip:5000) +最主要的一个库是 flask_cors,需要继续安装,命令:pip3 install -U flask-cors; +接口可以是:base_url, base_url + 'flask-route-method' , 对应方法中,return 便是返回给客户端的响应数据。 +Tips:这里有一个问题就是如果运行时直接是 app.run() 就是运行在本地,若是配置了 host 则可以运行在指定收信任的公网,具体可查看下面【flask 服务器实现代码】章节。 + +## 参考链接 + +[flask+Python+Vue实现前后端分离的web项目并部署至云服务器_flask项目打包并部署_chong墩儿的博客-CSDN博客](https://blog.csdn.net/qq_42244418/article/details/129363764) + +[Vue 和 Flask 前后端分离教程(一) - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/622418838) diff --git "a/_posts/2023-08-14-Ubuntu\345\210\233\345\273\272pycharm\347\232\204\345\277\253\346\215\267\346\226\271\345\274\217\344\270\244\347\247\215\346\226\271\346\263\225.md" "b/_posts/2023-08-14-Ubuntu\345\210\233\345\273\272pycharm\347\232\204\345\277\253\346\215\267\346\226\271\345\274\217\344\270\244\347\247\215\346\226\271\346\263\225.md" new file mode 100644 index 0000000000..6206b57a86 --- /dev/null +++ "b/_posts/2023-08-14-Ubuntu\345\210\233\345\273\272pycharm\347\232\204\345\277\253\346\215\267\346\226\271\345\274\217\344\270\244\347\247\215\346\226\271\346\263\225.md" @@ -0,0 +1,30 @@ +--- +layout: post +title: Ubuntu创建pycharm的快捷方式两种方法 +date: 2023-08-14 +description: "Ubuntu创建pycharm的快捷方式两种方法" +tag: Linux +--- +## 方法一 + +1. 打开终端,输入命令:`sudo gedit /usr/share/applications/pycharm.desktop`,创建pycharm.desktop文件 +2. 在pycharm.desktop文件中输入以下内容: + +```txt +[Desktop Entry] +Type=Application +Name=Pycharm +GenericName=Pycharm3 +Comment=Pycharm3:The Python IDE +Exec=sh /home/hugo/Downloads/pycharm-community-2023.1.3/bin/pycharm.sh +Icon=/home/hugo/Downloads/pycharm-community-2023.1.3/bin/pycharm.png +Terminal=pycharm +Categories=Pycharm; +``` + +## 方法二 + +1. 打开pycharm所在目录,运行命令:`./bin/pycharm.sh`,打开pycharm; +2. 点击tools; +3. 点击Create Desktop Entry; +4. add to favorites; diff --git "a/_posts/2023-08-14-flask\344\270\255\345\246\202\344\275\225\346\223\215\344\275\234\346\225\260\346\215\256\345\272\223.md" "b/_posts/2023-08-14-flask\344\270\255\345\246\202\344\275\225\346\223\215\344\275\234\346\225\260\346\215\256\345\272\223.md" new file mode 100644 index 0000000000..3dc426cd41 --- /dev/null +++ "b/_posts/2023-08-14-flask\344\270\255\345\246\202\344\275\225\346\223\215\344\275\234\346\225\260\346\215\256\345\272\223.md" @@ -0,0 +1,78 @@ +--- +layout: post +title: flask中如何操作数据库 +date: 2023-08-14 +description: "flask中如何操作数据库" +tag: 后端 +--- + + +## python中操作数据库 + +### 创建数据库 + +```python +# pip install flask-sqlalchemy +from flask_sqlalchemy import SQLAlchemy +from flask import Flask +# 连接mysql数据库需要导入pymysql +import pymysql + +# 创建flask对象 +app = Flask(__name__) +# 连接mysql +# app.config['SQLALCHEMY_DATABASE_URL']= 'mysql+pymysql://root:123456@localhost:3306/flask' +# 连接自带的sqlite数据库 +app.config['SQLALCHEMY_DATABASE_URL']='sqlite:///'+"/home/hugo/test.db" + +app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=False + +# 密钥 +app.config['SECRET_KEY']='*' + +# 实例化数据库对象 +db = SQLAlchemy(app) + +if __name__ == '__main__': + # 删除表 + db.drop_all() + # 创建表 + db.create_all() + # 运行flask + app.run(debug=True) +``` + + +## 通过模板进行交互 + +### Jinja2模板语言 + +```python + + +在项目文件夹下创建templates文件夹,用于存放html文件,比如说存放hello.html文件,这个模板文件示意如下: + +```html + +{% block title %}{% endblock %} - My Webpage +{% if name %} +

Hello, {{ name }}!

+{% else %} +

Hello, World!

+{% endif %} +{% block content %}{% endblock %} +``` + +在项目的主文件下,比如说app.py文件,示意如下: + +```python +from flask import Flask, render_template + +app = Flask(__name__) + +@app.route('/hello/') +@app.route('/hello/') +def hello(name=None): + return render_template('hello.html', name=name) +``` + diff --git "a/_posts/2023-08-17-MYSQL\345\205\245\351\227\250.md" "b/_posts/2023-08-17-MYSQL\345\205\245\351\227\250.md" new file mode 100644 index 0000000000..aa62542657 --- /dev/null +++ "b/_posts/2023-08-17-MYSQL\345\205\245\351\227\250.md" @@ -0,0 +1,131 @@ +--- +layout: post +title: MYSQL入门 +date: 2023-08-17 +description: "MYSQL入门 +tag: SQL +--- + +## 在ubuntu上安装MYSQL + +1. 打开终端,输入命令:`sudo apt-get install mysql-server`,安装mysql-server; +2. 查看mysql-server是否安装成功,输入命令:`sudo netstat -tap | grep mysql`,如果出现以下内容,则安装成功:(或者输入`sudo service mysql status`) + +```txt +tcp 0 0 localhost:mysql *:* LISTEN 2023/mysqld +``` + +3. 使用root用户登录mysql,输入命令:`sudo mysql -u root -p123456`; +4. 查看mysql的版本,输入命令:`mysql --version`。 + +## MYSQL基本设置 + +`sudo mysql_secure_installation` 是一个用于加强MySQL安全性的命令。它会引导您完成一系列安全设置,包括设置root密码、删除匿名用户、禁用远程root登录等。 + +### 重置密码 + +1. 停止mysql服务:`sudo service mysql stop`; +2. 进入mysql配置文件的目录:`cd /etc/mysql/`,发现mysql.cnf文件; +3. 修改mysql.cnf文件:`sudo vim mysql.cnf`,没有找到[mysqld]; + +mysql.cnf内容如下: + +```cnf +# +# The MySQL database server configuration file. +# +# You can copy this to one of: +# - "/etc/mysql/my.cnf" to set global options, +# - "~/.my.cnf" to set user-specific options. +# +# One can use all long options that the program supports. +# Run program with --help to get a list of available options and with +# --print-defaults to see which it would actually understand and use. +# +# For explanations see +# http://dev.mysql.com/doc/mysql/en/server-system-variables.html + +# +# * IMPORTANT: Additional settings that can override those from this file! +# The files must end with '.cnf', otherwise they'll be ignored. +# +# +!includedir /etc/mysql/conf.d/ +!includedir /etc/mysql/mysql.conf.d/ +``` + +4. 说明应该是mysql.cnf文件被分割成了多个文件,所以需要修改`/etc/mysql/mysql.conf.d/mysqld.cnf`文件; +5. 修改`/etc/mysql/mysql.conf.d/mysqld.cnf`文件,找到[mysqld],在下面添加`skip-grant-tables`,保存退出; +6. 在终端输入命令:`sudo service mysql start`,启动mysql服务,然后再输入命令:`sudo mysql`,回车后提示输入密码,直接回车,进入mysql; +7. 进入mysql后,输入命令:`use mysql;`,回车后提示`Database changed`,说明已经进入mysql数据库; +8. 修改root用户的密码,输入命令:`update user set authentication_string=password('123456') where user='root';`,将root用户密码修改为123456; +9. 刷新权限,输入命令:`flush privileges;`,回车后提示`Query OK, 0 rows affected (0.00 sec)`,说明刷新权限成功; +10. 退出mysql,输入命令:`exit;`,回车后提示`Bye`,说明退出mysql成功; +11. 重启mysql服务,输入命令:`sudo service mysql restart`,回车后提示`mysql stop/waiting`,说明mysql服务已经停止,然后再输入命令:`sudo service mysql start`,回车后提示`mysql start/running, process 2023`,说明mysql服务已经启动; + +## MYSQL基本操作 + +### 创建数据库 + +进入数据库: + +```shell +mysql -uroot -p123456 +``` + +```sql +create database test; +show databases; +USE test; +show tables; +``` + +### 删除数据库 + +```sql +drop database test; +``` + +### 创建表 + +```sql +CREATE mywind_raw; # 创建表`mywind_raw` +# or specific the name and constrain of the column +CREATE TABLE StudentsInfo( + id INT(11) NOT NULL AUTO_INCREMENT, + name VARCHAR(20) NOT NULL, + age INT(11) NOT NULL, + PRIMARY KEY(id) +); +``` + +在名为"music"的数据库中创建一个名为"song_info"的表格,并包含"歌曲名"、“歌手信息”、"专辑"和"发行年份"这四个字段:(使用VARCHAR类型,最大长度为255;发行年份,使用INT类型) + +```sql +USE music; +CREATE TABLE song_info ( + song_name VARCHAR(255), + artist_info VARCHAR(255), + album VARCHAR(255), + release_year INT +); +# 查看表的定义 +desc song_info; +# +--------------+--------------+------+-----+---------+-------+ +# | Field | Type | Null | Key | Default | Extra | +# +--------------+--------------+------+-----+---------+-------+ +# | song_name | varchar(255) | YES | | NULL | | +# | artist_info | varchar(255) | YES | | NULL | | +# | album | varchar(255) | YES | | NULL | | +# | release_year | int | YES | | NULL | | +# +--------------+--------------+------+-----+---------+-------+ +# 4 rows in set (0.01 sec) +``` + +虽然desc 命令可以查看表定义,但是其输出的信息还是不够全面,为了查看更全面的表定 +义信息,有时就需要通过查看创建表的SQL 语句来得到: + +```sql +SHOW CREATE TABLE song_info \G; +``` + diff --git "a/_posts/2023-08-18-postman\344\270\213\350\275\275\345\256\211\350\243\205.md" "b/_posts/2023-08-18-postman\344\270\213\350\275\275\345\256\211\350\243\205.md" new file mode 100644 index 0000000000..b08fc46fc8 --- /dev/null +++ "b/_posts/2023-08-18-postman\344\270\213\350\275\275\345\256\211\350\243\205.md" @@ -0,0 +1,94 @@ +--- +layout: post +title: postman下载安装 +date: 2023-08-18 +description: "postman下载安装" +tag: Linux +--- +## postman下载 + +[postman官网](https://www.postman.com/downloads/) + +或者通过snap安装 + +```bash +snap install postman +``` + +从官网下载之后,下载文件为 `postman-linux-x64.tar.gz` + +解压缩: + +```bash +tar -zxvf postman-linux-x64.tar.gz +# move to /opt +sudo mv Postman /opt +``` + +或者直接指定解压缩目录 + +```bash +tar -zxvf postman-linux-x64.tar.gz -C /opt +``` + +## 运行 + +```bash +cd /opt/ +./Postman/Postman +``` + +## 运行白屏解决 + +可能是环境变量的问题: + + +设置临时环境变量验证一下:`export POSTMAN_DISABLE_GPU=true` + +运行发现问题解决了: + +`sudo gedit /etc/profile` + +末尾加入: + +> `export POSTMAN_DISABLE_GPU=true` +> `export PATH=$PATH:$POSTMAN_DISABLE_GPU` + +## 创建快捷方式 + +创建软链接: + +```bash +sudo ln -s /opt/Postman/Postman /usr/bin/postman +``` + +创建快速启动: + +```bash +sudo vim /usr/share/applications/postman.desktop +``` + +输入以下内容: + +```txt +[Desktop Entry] +Encoding=UTF-8 +Name=Postman +Exec=postman +Icon=/opt/Postman/app/resources/app/assets/icon.png +Terminal=false +Type=Application +Categories=Development; +``` + +赋予可执行权限 + +```bash +sudo chmod +x /usr/share/applications/postman.desktop +``` + +复制到桌面 + +```bash +cp /usr/share/applications/postman.desktop ~/Desktop +``` diff --git "a/_posts/2023-09-04-threeJS\345\205\245\351\227\250.md" "b/_posts/2023-09-04-threeJS\345\205\245\351\227\250.md" new file mode 100644 index 0000000000..8bb1b8e6f2 --- /dev/null +++ "b/_posts/2023-09-04-threeJS\345\205\245\351\227\250.md" @@ -0,0 +1,249 @@ +--- +layout: post +title: threeJS入门 +date: 2023-09-04 +description: "threeJS入门" +tag: 前端 +--- +# WebGL和three.js + +WebGL是在浏览器中实现三维效果的一套规范。three.js是基于WebGL实现的一个三维开源库,可以很方便地实现3D场景和动画。 + +# 创建3D图形 + +场景(scene) +相机(camera):人的眼睛 +渲染器(renderer) + +![20230906095341](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230906095341.png) + +![20230906095415](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230906095415.png) + +3D世界是由点组成,两个点能够组成一条直线,三个不在一条直线上的点就能够组成一个三角形面,无数三角形面就能够组成各种形状的物体。 + +## 点的定义 + +```js +// 创建一个点 +var point = new THREE.Vector3(1, 2, 3); +// or using set +var point2 = new THREE.Vector3(); +point2.set(1, 2, 3); +``` + +## 线的定义 + +在Threejs中,一条线由点,材质和颜色组成。 + +THREE.Geometry形状包含一个数组vertices,这个vertices就是存放无数的点(THREE.Vector3)的数组。 + +也可以使用专为线准备的材质,THREE.LineBasicMaterial。 + +```js +var p1 = new THREE.Vector3(-100, 0, 100); +var p2 = new THREE.Vector3(100, 0, -100); +// 声明一个Geometry +var geometry = new THREE.Geometry(); +geometry.vertices.push(p1); +geonetry.vertice.push(p2); +// LineBasicMaterial +// 创建一个 THREE.LineBasicMaterial 来定义线段的样式。你可以设置线段的颜色、线宽等属性 +var material = new THREE.LineBasicMaterial({ color: 0xff0000, linewidth: 2 }); +var line = new THREE.Line(geometry, material); +scene.add(line); +``` + +## 三角形面的定义 + +```js +// 定义三角形面的三个顶点 +var p1 = new THREE.Vector3(-100, 0, 100); +var p2 = new THREE.Vector3(100, 0, -100); +var p3 = new THREE.Vector3(-100, 0, -100); +// 声明一个Geometry +var faceGeometry = new THREE.Geometry(); +// MeshBasicMaterial是一种材质,用于定义网格模型的颜色,side属性用于定义网格模型的双面或单面渲染 +var faceMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, side: THREE.DoubleSide }); +// 将三个顶点添加到Geometry中 +faceGeometry.vertices.push(p1); +faceGeometry.vertices.push(p2); +faceGeometry.vertices.push(p3); + +var face = THREE.Mesh(faceGeometry, faceMaterial); + +scene.add(face); +``` + +## 圆 + +这段代码创建了一个圆形的网格对象(Mesh),使用了 THREE.CircleGeometry 来定义圆的几何形状,以及 THREE.MeshBasicMaterial 来指定圆的材质。 + +THREE.CircleGeometry 的参数解释如下: + +第一个参数是圆的半径; +第二个参数是圆的分段数,决定了圆的平滑度; +第三个参数是圆的起始角度; +第四个参数是圆的结束角度。 + +起始角度是 0,结束角度是 Math.PI * 2,表示一个完整的圆。 + +创建一个圆环与之类似,只是起始角度和结束角度不太一样。 + +```js +// 创建一个圆 +var circleGeometry = new THREE.CircleGeometry(50, 20, 0, Math.PI * 2); + +var circleMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, side: THREE.DoubleSide }); + +//移除圆的中心点 +circleGeometry.vertices.shift(); + +var circle = new THREE.Mesh(circleGeometry, circleMaterial); + +scene.add(circle); +``` + +## 动态场景 + +第一种方法是让物体在坐标系里面移动,摄像机不动。 +第二种方法是让摄像机在坐标系里面移动,物体不动。 + +移动物体示意代码: + +```js +// 创建场景、渲染器和摄像机,省略代码... + +// 创建一个立方体 +var cubeGeometry = new THREE.BoxGeometry(1, 1, 1); +var cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); +var cube = new THREE.Mesh(cubeGeometry, cubeMaterial); + +// 将立方体添加到场景中 +scene.add(cube); + +// 动画循环 +function animate() { + requestAnimationFrame(animate); + + // 更新立方体的位置 + cube.position.x += 0.01; + + // 渲染场景 + renderer.render(scene, camera); +} + +// 启动动画循环 +animate(); +``` + +更新摄像机示意代码: + +```js +// 创建场景、渲染器和摄像机,省略代码... +// 创建一个立方体 +var cubeGeometry = new THREE.BoxGeometry(1, 1, 1); +var cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); +var cube = new THREE.Mesh(cubeGeometry, cubeMaterial); + +// 将立方体添加到场景中 +scene.add(cube); + +// 动画循环 +function animate() { + requestAnimationFrame(animate); + + // 更新摄像机的位置 + camera.position.x += 0.01; + + // 渲染场景 + renderer.render(scene, camera); +} + +// 启动动画循环 +animate(); +``` + +在Three.js中,当我们改变了物体的位置、颜色或其他属性时,需要重新调用 renderer.render(scene, camera) 函数,以便将更新后的场景重新绘制到浏览器中。 + +但是,在WebGL中,渲染是一个昂贵的操作,如果我们在每次更新之后都手动调用 renderer.render(scene, camera),那么会造成性能上的浪费。 + +为了避免这种情况,我们可以使用 requestAnimationFrame 函数来实现自动刷新场景。requestAnimationFrame 是一个JavaScript的特殊函数,它会在浏览器每次绘制新的一帧之前调用我们指定的回调函数。通过这种方式,我们不需要手动调用 renderer.render(scene, camera),而是通过 requestAnimationFrame 自动触发渲染,并确保场景及时更新。 + +也就是说,requestAnimationFrame 函数会自动处理帧率和浏览器的优化机制,只有在下一帧绘制时,才会执行 renderer.render(scene, camera) 以更新场景。 + +## 相机与投影 + +正交投影(Orthographic Projection): +正交投影是一种平行投影方式,它不考虑物体与相机之间的距离,所有物体在投影面上的大小都是一样的。正交投影在Three.js中使用 OrthographicCamera 来实现。 + +透视投影(Perspective Projection): +透视投影是一种模拟人眼观察场景的投影方式。它通过将远处的物体缩小,近处的物体放大,来创造出远近不同的景深效果。透视投影在Three.js中使用 PerspectiveCamera 来实现。 + +![20230906102858](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230906102858.png) + +正交投影代码实现: + +相机的视图范围由 left、right、top 和 bottom 参数决定,而 near 和 far 参数定义了相机的视锥体的近平面和远平面距离。 + +```js +OrthographicCamera( left, right, top, bottom, near, far ) +var camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 1000 ); +scene.add( camera ); +``` + +透视投影代码: + +```js +var camera = new THREE.PerspectiveCamera(fov, aspect, near, far); +scene.add(camera); +``` + +fov:视场角(Field of View),表示相机在垂直方向上可见范围的角度。 +aspect:视图宽度和高度的比例。 +near:相机视锥体近平面距离。 +far:相机视锥体远平面距离。 + +![20230906103447](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230906103447.png) + +透视投影可以呈现出远近景深感,而正交投影则是保持物体尺寸一致的平行投影方式。 + +人类的正常视角是120度集中注意力看清楚,在30-40度 + +## 光源 + +光源可以为你的场景提供照明效果,使物体的材质能够反射、投射或接收光线。Three.js提供了一个基类THREE.Light,包含多种光源类型,包括环境光(AmbientLight)、平行光(DirectionalLight)、点光源(PointLight)等。你可以根据场景的需求添加适当的光源。 + +### 环境光源 + +环境光就是在场景中无处不在的光,它对物体的影响是均匀的,也就是无论你从物体的那个角度观察,物体的颜色都是一样的! + +```js +var light = new THREE.AmbientLight(0xffffff); +scene.add(light); +``` + +![20230906103650](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230906103650.png) + +### 点光源 + +点光源就像一个灯泡,它会向四周发出光线,光线的强度会随着距离的增加而衰减。 + +```js +var light = new THREE.PointLight(0xffffff, 1, 1000); +scene.add(light); +``` + +第一个参数是光源的颜色;第二个参数是光源的强度,默认是1.0,表示100%强度的光;第三个参数表示光的距离,从光源所在的位置,经过distance这段距离之后,光的强度将从Intensity衰减为0。 + +### 聚光灯 + +- THREE.SpotLight( hex, intensity, distance, angle, exponent ) +- Hex:聚光灯发出的颜色,如0xFFFFFF +- Intensity:光源的强度,默认是1.0,如果为0.5,则强度是一半 +- Distance:光线的强度,从最大值衰减到0,需要的距离。 默认为0,表示光不衰减,如果非0,则表示从光源的位置到Distance的距离,光都在线性衰减。到离光源距离Distance时,光源强度为0. +- Angle:聚光灯着色的角度,用弧度作为单位,这个角度是和光源的方向形成的角度。 +- exponent:光源模型中,衰减的一个参数,越大衰减约快。 + +# 参考链接 + +- 李春芳 《数据可视化》 diff --git "a/_posts/2023-09-04-vue\345\222\214threeJS\351\241\271\347\233\256\345\256\236\350\267\265.md" "b/_posts/2023-09-04-vue\345\222\214threeJS\351\241\271\347\233\256\345\256\236\350\267\265.md" new file mode 100644 index 0000000000..7aa749adde --- /dev/null +++ "b/_posts/2023-09-04-vue\345\222\214threeJS\351\241\271\347\233\256\345\256\236\350\267\265.md" @@ -0,0 +1,129 @@ +--- +layout: post +title: vue和threeJS项目实践 +date: 2023-09-04 +description: "vue和threeJS项目实践" +tag: 前端 +--- + +# 创建项目 + +```bash +nvm use 12 +npm init vite@latest +# 选择vue + javascript +cd ProjectName +code . +npm install +npm install three +npm run dev +``` + +# 创建最简单的三元素 + +三元素: + +1. 场景(物体) +2. 摄像机(相当于眼镜) +3. 渲染器 + +```js + +``` + +现在的页面是一个黑黑的页面,因为我们还没有添加任何物体。同时,修改`style.css`,(删除多余的样式,只保留body的margin为0)让canvas占满整个页面,而不是出现滚动条。 + +效果如下: + +![20230906114859](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230906114859.png) + +# 创建物体 + +```js +// set background color,ox79deec is a light blue,0x表示16进制 +renderer.setClearColor(0X79deec); + +// 4 创建一个立方体,添加到场景中 +const mygeometry = new THREE.BoxGeometry(); +// 创建材质 +const material = new THREE.MeshBasicMaterial({ + color: 0xff0000, + // 设置线条呈现 + wireframe: true +}); +//把材质渲染到立方体上, +const cube = new THREE.Mesh(mygeometry, material); +// 把立方体添加到场景中 +scene.add(cube); +// 一定要设置相机的位置,否则看不到物体,否则摄像机都在原点上(0,0,0) +// x轴左右;y轴上下,z轴指向我们,我们需要把摄像机的z轴移动到靠近我们位置 +camera.position.z = 5; +``` + +这时候,可以看到我们刚刚加进去的cube: + +![20230906135823](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230906135823.png) + +可以先把cube旋转一下: + +```js +cube.rotation.y = 1; +// 拉高摄像头位置 +camera.position.y = 5; +``` + +现在画面如下: + +![20230906141053](https://cdn.jsdelivr.net/gh/ChanJeunlam/PicgoBed/blogs/pictures/20230906141053.png) + + +# 添加轨道控制器 + +为乐使得摄像机位置改变不影响物体在页面上的位置,需要添加一个轨道控制器: + +```vue +import { OrbitControls} from "three/examples/jsm/controls/OrbitControls" + +//5 创建轨道控制器 + +const controls = new OrbitControls(camera,render.domElement); +``` + +# 添加动画 + + +```js +const axesHelper = new THREE.AxesHelper(5); +scene.add(axesHelper); +// 辅助网格 +const gridHelper = new THREE.GridHelper(10, 10); // size,divisions +scene.add(gridHelper); +``` + + + + + diff --git "a/_posts/2023-09-04-\344\275\277\347\224\250threeJS\347\273\230\345\210\266\344\270\211\347\273\264\345\217\257\350\247\206\345\214\226\345\234\260\345\233\276.md" "b/_posts/2023-09-04-\344\275\277\347\224\250threeJS\347\273\230\345\210\266\344\270\211\347\273\264\345\217\257\350\247\206\345\214\226\345\234\260\345\233\276.md" new file mode 100644 index 0000000000..927a34ee37 --- /dev/null +++ "b/_posts/2023-09-04-\344\275\277\347\224\250threeJS\347\273\230\345\210\266\344\270\211\347\273\264\345\217\257\350\247\206\345\214\226\345\234\260\345\233\276.md" @@ -0,0 +1,15 @@ +--- +layout: post +title: 使用threeJS绘制三维可视化地图 +date: 2023-09-04 +description: "使用threeJS绘制三维可视化地图" +tag: 前端 +--- + +# 球体几何对象 + +# 材质:地理情况图 + +# 坐标+连线+动画 + +# 渲染文字 \ No newline at end of file diff --git "a/_posts/2023-09-04-\346\225\260\345\255\227\345\214\226\345\244\247\345\261\217\346\200\273\347\273\223.md" "b/_posts/2023-09-04-\346\225\260\345\255\227\345\214\226\345\244\247\345\261\217\346\200\273\347\273\223.md" new file mode 100644 index 0000000000..c9aabf559c --- /dev/null +++ "b/_posts/2023-09-04-\346\225\260\345\255\227\345\214\226\345\244\247\345\261\217\346\200\273\347\273\223.md" @@ -0,0 +1,165 @@ +--- +layout: post +title: 数字化大屏UI设计总结 +date: 2023-09-04 +description: "数字化大屏UI设计总结" +tag: UI +--- + +# 1 需求调研 + +## 1.1 根据业务场景提炼关键数据 + +较为正式的大屏实施项目一般有需求文档,在拿到需求文档后,还需要与需求方沟通大屏业务场景,明确需要侧重展示的内容或大屏要呈现的“故事”,从而提炼关键数据指标。 + +举例子: + +某制造行业的监控指挥中心大屏,位于一个大型指挥管控中心,需要监控到的数据一般包括**销售运营**、**生产运行**、**物流管理**、**客户质量**和园区的**安保能耗**这5个模块,这些业务关键数据。 + + +## 1.2 确定数据指标的优先级 + +从需求文档或和业务交流获取到关键数据后,再确定提炼出的数据指标的优先级: + +主要信息呈现业务的主要逻辑,一般放在显眼位置、用重点元素标识;次要信息围绕主要信息进一步阐述;辅助信息是主要信息的补充,放在角落位置,或者鼠标悬浮显示。 + +## 1.3 确定数据指标的展示形式 + +大屏可视化元素可分为指标卡、表格、图表(仪表盘)三类。 + +常见的展示图表包括**柱状图**,**饼图**,**折线图**,**轮播图**,**雷达图**等等。不同数据适用于不同的图表类型。 + + +## 1.4 确定大屏分辨率 + +大屏的设计要基于最终显示的分辨率要求去做,分辨率受大屏模板分辨率、**显卡输出分辨率**和**大屏幕硬件支持**的分辨率三方面影响。 + +有时候,还需要注意大屏的拼接方法(9块屏幕拼接成1块,还是16块屏幕拼接成1块),是LCD屏还是LED屏幕 + +模板分辨率的“正确”包括宽高比和高清度两个要素,一般的大屏在宽高比上有16:9和32:9两种,用错比例会导致最终的投屏效果变形或填不满屏幕。 + +根据展示设备的分辨率,32:9的一般做3840*1080的模板,而16:9的模板则可以做960*540的普通屏、1280*960的高清屏、1920*1080的超清屏。 + + +# 2 视觉设计 + +视觉设计需要关注布局、配色、字体和组件样式4个方面。 + +## 2.1 布局方案 + +根据数据需求阶段规划提炼的数据指标,一个大屏一般划分出3-5个大模块。 + +主模块布局根据业务关键数据划分为3到5个部分。再将每个大模块再拆分为更多小模块,每个小模块里有一个或多个组件,用来容纳需求调研中确定的主、次、辅信息。最后,根据需求规划中确定的可视化元素依次填充子模块,让大屏的整体布局更具象。 + +### 2.2 布局结构 + + + +## 2.2 配色方案 + +### 2.2.1 主配色 + +大屏的整体配色,奠定了大屏的场景基调,一般是画布的背景、组件的背景。 + +一般来说,科技类的大屏的配色方案主要是蓝色和绿色,以下是以蓝色调为主色调的配色方案: + + +### 2.2.2 预警色 + +一些需要特别突出的指标,或指标值达到预警值时的颜色,一般比较明亮,且与大屏主配色有较为显著的对比。如果结合图表闪烁动画、警戒线等共同设置,效果更佳。 + + + +### 2.2.3 图表系列色 + +不同展示逻辑的图表可能需要不同的配色方案,比如柱状图需要有明显的对比色,以便于用户区分。 + +#### 2.2.3.1 对比系列色 + +对比系列色常用于多系列的柱形图、条形图和饼图等,强调数据系列之间的差异。 + + + +#### 2.2.3.2 连续系列色 + +常用于单系列的柱形图、条形图、面积图等,在大屏整体看来只有“一个颜色”,但渐变的效果不仅表达了数据之间的连续性,还有酷炫的视觉效果。“连续”的效果可以通过渐进的一系列颜色或渐变色体现。 + + + +### 2.2.4 配色获取方法 + +1 + +[ColorDrop](https://www.colordrop.io/) + +[Fresh Background Gradients | WebGradients.com 💎](https://webgradients.com/) + +[zhongguose - 传统颜色](http://zhongguose.com/) + +## 2.3 字体方案 + +字体方案包括字体类型、字号、字重和配色4个方面: + +* 字体类型不宜超过两种,大屏上尽量用有衬线字体,并注意字体可商用问题; +* 字号在不同等级的标题、正文、图表字体之间要有区分; +* 高级别的标题、突出的指标、表格表头等一般需要加重字体; +* 大屏字体基本为浅色、亮色,结合整体配色方案做规划。 + + +一般来说,各种组件设置如下: + +组件标题:字号:28 字体:默认字体、居左、上下居中对齐 + +组件图例:字号:16 + +组件坐标:字号:18 + +组件数据:字号:20(不要加粗) + +## 2.4 组件方案 + +组件方案是指大屏使用什么样式的组件素材,主要考虑大标题、组件标题、大屏背景图、组件背景框和指标卡样式。这些视觉素材对大屏的整体效果有很大的影响,需要做好合理的规划。 + +## 2.5 整体风格 + +## 2.7 动态 + +动效应优先满足核心内容、故事线。常见的大屏动效 - 展示类,用于突出产品核心功能和特点。界面信息按照一定的规律呈现,引导用户的视觉流向。 + +# 3 技术选型和开发调试 + +用的比较多的就是JS+Ecahrts,还可以直接用可视化工具制作,比如FineReport、dataV等等。 + +## 3.1 + +## 3.2 + +## 3.3 制作大屏3D地图 + +### 3.3.1 制作3D国家地图 + +下载矢量地图的网站: + +阿里云DataV: + +[DataV.GeoAtlas地理小工具系列 (aliyun.com)](https://datav.aliyun.com/portal/school/atlas/area_selector) + +国家地理信息公共服务平台-标准地图服务: + +[标准地图服务系统 (mnr.gov.cn)](http://bzdt.ch.mnr.gov.cn/) + +下载好SVG图片之后,导入到axure中,鼠标右键-变化图片-转换SVG图片为形状-对齐进行编辑和修改。 + +### 3.3.2 制作三D地球模型 + +地球:粒子地球、地图贴图、地球模型。 + +# 4 大屏调试 + +在完成设计之后,要再次校验信息层级、文字大小、图表等各层级间的对比关系是否传达准确,与技术同步沟通下技术的实现性。最后开发完成后,要拿演示demo去现场测试,看下整体展示效果,测试输出是否有问题,有无拉伸问题,拼接缝与内容有无穿插,及时与开发进行页面的校验工作,最终才算是设计完成。 + +# 参考链接 + +[收藏|盈析数据手把手教你设计可视化数字大屏 (qq.com)](https://mp.weixin.qq.com/s/3OyYKkgl6wjZLvsB0RNIyA) + +[「大屏开发指南」手把手教你做大屏,附素材! (qq.com)](https://mp.weixin.qq.com/s/3hwUtQKhAbfOgCmtl4C0kg) \ No newline at end of file diff --git "a/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677199736884.png" "b/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677199736884.png" new file mode 100644 index 0000000000..01c161c6e3 Binary files /dev/null and "b/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677199736884.png" differ diff --git "a/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677199916004.png" "b/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677199916004.png" new file mode 100644 index 0000000000..12a2549281 Binary files /dev/null and "b/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677199916004.png" differ diff --git "a/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677200253300.png" "b/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677200253300.png" new file mode 100644 index 0000000000..99dfff38b8 Binary files /dev/null and "b/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677200253300.png" differ diff --git "a/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677200490189.png" "b/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677200490189.png" new file mode 100644 index 0000000000..e489acd685 Binary files /dev/null and "b/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677200490189.png" differ diff --git "a/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677201879487.png" "b/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677201879487.png" new file mode 100644 index 0000000000..ea95b0c183 Binary files /dev/null and "b/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677201879487.png" differ diff --git "a/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677202138195.png" "b/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677202138195.png" new file mode 100644 index 0000000000..7c2f9c7cd2 Binary files /dev/null and "b/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677202138195.png" differ diff --git "a/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677202388976.png" "b/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677202388976.png" new file mode 100644 index 0000000000..298f386c07 Binary files /dev/null and "b/_posts/image/2023-02-21-Ubuntu\345\256\211\350\243\205MySQL8.0\345\271\266\350\277\234\347\250\213\350\277\236\346\216\245/1677202388976.png" differ diff --git "a/_posts/image/2023-03-16-\345\210\244\346\226\255\346\230\257\345\220\246\346\227\266\347\264\240\346\225\260\345\207\275\346\225\260\347\232\204\345\215\225\345\205\203\346\265\213\350\257\225/1678936036418.png" "b/_posts/image/2023-03-16-\345\210\244\346\226\255\346\230\257\345\220\246\346\227\266\347\264\240\346\225\260\345\207\275\346\225\260\347\232\204\345\215\225\345\205\203\346\265\213\350\257\225/1678936036418.png" new file mode 100644 index 0000000000..a3af2af830 Binary files /dev/null and "b/_posts/image/2023-03-16-\345\210\244\346\226\255\346\230\257\345\220\246\346\227\266\347\264\240\346\225\260\345\207\275\346\225\260\347\232\204\345\215\225\345\205\203\346\265\213\350\257\225/1678936036418.png" differ diff --git "a/_posts/image/2023-03-16-\345\210\244\346\226\255\346\230\257\345\220\246\346\227\266\347\264\240\346\225\260\345\207\275\346\225\260\347\232\204\345\215\225\345\205\203\346\265\213\350\257\225/1678936579647.png" "b/_posts/image/2023-03-16-\345\210\244\346\226\255\346\230\257\345\220\246\346\227\266\347\264\240\346\225\260\345\207\275\346\225\260\347\232\204\345\215\225\345\205\203\346\265\213\350\257\225/1678936579647.png" new file mode 100644 index 0000000000..a026b2d0ba Binary files /dev/null and "b/_posts/image/2023-03-16-\345\210\244\346\226\255\346\230\257\345\220\246\346\227\266\347\264\240\346\225\260\345\207\275\346\225\260\347\232\204\345\215\225\345\205\203\346\265\213\350\257\225/1678936579647.png" differ diff --git "a/_posts/image/2023-04-04-\345\244\252\346\271\226\347\253\2312001-2006\345\271\264\346\260\264\350\264\250\347\233\221\346\265\213\346\225\260\346\215\256\345\210\206\346\236\220/1680608067436.png" "b/_posts/image/2023-04-04-\345\244\252\346\271\226\347\253\2312001-2006\345\271\264\346\260\264\350\264\250\347\233\221\346\265\213\346\225\260\346\215\256\345\210\206\346\236\220/1680608067436.png" new file mode 100644 index 0000000000..59952896da Binary files /dev/null and "b/_posts/image/2023-04-04-\345\244\252\346\271\226\347\253\2312001-2006\345\271\264\346\260\264\350\264\250\347\233\221\346\265\213\346\225\260\346\215\256\345\210\206\346\236\220/1680608067436.png" differ diff --git "a/_posts/image/2023-04-06-\351\243\216\351\200\237\351\242\204\346\265\213/1680768137267.png" "b/_posts/image/2023-04-06-\351\243\216\351\200\237\351\242\204\346\265\213/1680768137267.png" new file mode 100644 index 0000000000..55c24ac5f3 Binary files /dev/null and "b/_posts/image/2023-04-06-\351\243\216\351\200\237\351\242\204\346\265\213/1680768137267.png" differ diff --git "a/_posts/image/2023-04-06-\351\243\216\351\200\237\351\242\204\346\265\213/1680770834766.png" "b/_posts/image/2023-04-06-\351\243\216\351\200\237\351\242\204\346\265\213/1680770834766.png" new file mode 100644 index 0000000000..f5bb366b12 Binary files /dev/null and "b/_posts/image/2023-04-06-\351\243\216\351\200\237\351\242\204\346\265\213/1680770834766.png" differ diff --git a/about.md b/about.md index 6b694fd7b2..0dbd39f75b 100755 --- a/about.md +++ b/about.md @@ -3,31 +3,9 @@ layout: page title: 关于我 --- -一个工作6年的 iOS SDK 开发者,在一家游戏公司做SDK开发和维护。 +一个学不会地球流体力学的物理海洋学生瓜蛋子。 -除了iOS技术,我还一直维护着这个博客,懂一些前端的基础知识。 -平时喜欢用python写一些工具,能用工具做的事情坚决不用人工去操作。 +

为啥想写博客

-

我们的博客

- -这个博客是我们大家的,目前已经有很大一部分人在使用我的博客模板了,我也很高兴大家使用我的模板。 - -如果你想搭建一个跟我一样的博客,可以看我的 - Jekyll 搭建个人博客 -教程 - - -有关于博客主题的建议和意见都可以提给我,让我们一起来打造一个精美的主题吧~ - -博客源码在 Github 上,你的 Star 是我更新的动力,谢谢~ - - -

搭建博客遇到了问题解法方法

- -查看 [技术支持](http://leopardpan.cn/support/) 需求帮助 - -博客模板会一直持续更新,请持续关注我,谢谢~ - -{% include comments.html %} diff --git "a/code/.ipynb_checkpoints/2022-07-11-D-Tale\345\272\223\344\273\213\347\273\215-checkpoint.ipynb" "b/code/.ipynb_checkpoints/2022-07-11-D-Tale\345\272\223\344\273\213\347\273\215-checkpoint.ipynb" new file mode 100644 index 0000000000..83e777a4d5 --- /dev/null +++ "b/code/.ipynb_checkpoints/2022-07-11-D-Tale\345\272\223\344\273\213\347\273\215-checkpoint.ipynb" @@ -0,0 +1,56 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.3" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "vp": { + "vp_config_version": "1.0.0", + "vp_menu_width": 273, + "vp_note_display": false, + "vp_note_width": 0, + "vp_position": { + "width": 278 + }, + "vp_section_display": false, + "vp_signature": "VisualPython" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git "a/code/2022-07-11-D-Tale\345\272\223\344\273\213\347\273\215.ipynb" "b/code/2022-07-11-D-Tale\345\272\223\344\273\213\347\273\215.ipynb" new file mode 100644 index 0000000000..83e777a4d5 --- /dev/null +++ "b/code/2022-07-11-D-Tale\345\272\223\344\273\213\347\273\215.ipynb" @@ -0,0 +1,56 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.3" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "vp": { + "vp_config_version": "1.0.0", + "vp_menu_width": 273, + "vp_note_display": false, + "vp_note_width": 0, + "vp_position": { + "width": 278 + }, + "vp_section_display": false, + "vp_signature": "VisualPython" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/images/avatar.jpg b/images/avatar.jpg index 524ad69fb2..2513539a37 100644 Binary files a/images/avatar.jpg and b/images/avatar.jpg differ diff --git a/images/payimg/alipayimg.jpg b/images/payimg/alipayimg.jpg index c966c0e145..3f3358833d 100644 Binary files a/images/payimg/alipayimg.jpg and b/images/payimg/alipayimg.jpg differ diff --git a/images/payimg/weipayimg.jpg b/images/payimg/weipayimg.jpg index a6ce0fe9aa..f4537f3e9b 100644 Binary files a/images/payimg/weipayimg.jpg and b/images/payimg/weipayimg.jpg differ diff --git "a/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/Copernicus Marine Service.jpg" "b/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/Copernicus Marine Service.jpg" new file mode 100644 index 0000000000..9e89bc491b Binary files /dev/null and "b/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/Copernicus Marine Service.jpg" differ diff --git "a/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/code.jpg" "b/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/code.jpg" new file mode 100644 index 0000000000..7ee50283b8 Binary files /dev/null and "b/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/code.jpg" differ diff --git "a/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\344\270\213\350\275\275\346\214\211\351\222\256.jpg" "b/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\344\270\213\350\275\275\346\214\211\351\222\256.jpg" new file mode 100644 index 0000000000..cde2d231ef Binary files /dev/null and "b/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\344\270\213\350\275\275\346\214\211\351\222\256.jpg" differ diff --git "a/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\345\205\250\347\220\203\344\270\234\350\245\277\345\220\221\346\265\201\351\200\237.jpg" "b/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\345\205\250\347\220\203\344\270\234\350\245\277\345\220\221\346\265\201\351\200\237.jpg" new file mode 100644 index 0000000000..4bd77e30db Binary files /dev/null and "b/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\345\205\250\347\220\203\344\270\234\350\245\277\345\220\221\346\265\201\351\200\237.jpg" differ diff --git "a/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\346\225\260\346\215\256\344\273\213\347\273\215.jpg" "b/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\346\225\260\346\215\256\344\273\213\347\273\215.jpg" new file mode 100644 index 0000000000..c07ebcae89 Binary files /dev/null and "b/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\346\225\260\346\215\256\344\273\213\347\273\215.jpg" differ diff --git "a/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\346\225\260\346\215\256\345\234\250\347\272\277\346\237\245\347\234\213.jpg" "b/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\346\225\260\346\215\256\345\234\250\347\272\277\346\237\245\347\234\213.jpg" new file mode 100644 index 0000000000..e93f25f8ca Binary files /dev/null and "b/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\346\225\260\346\215\256\345\234\250\347\272\277\346\237\245\347\234\213.jpg" differ diff --git "a/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\346\225\260\346\215\256\346\243\200\347\264\242.jpg" "b/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\346\225\260\346\215\256\346\243\200\347\264\242.jpg" new file mode 100644 index 0000000000..97cbdfe33f Binary files /dev/null and "b/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\346\225\260\346\215\256\346\243\200\347\264\242.jpg" differ diff --git "a/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\346\225\260\346\215\256\350\214\203\345\233\264.jpg" "b/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\346\225\260\346\215\256\350\214\203\345\233\264.jpg" new file mode 100644 index 0000000000..1f98fa0bda Binary files /dev/null and "b/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\346\225\260\346\215\256\350\214\203\345\233\264.jpg" differ diff --git "a/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\346\246\202\345\277\265\345\233\276.png" "b/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\346\246\202\345\277\265\345\233\276.png" new file mode 100644 index 0000000000..7b5bdbbdfa Binary files /dev/null and "b/images/posts/2022-08-09-\346\265\267\350\241\250\351\235\242\351\253\230\345\272\246\346\225\260\346\215\256\344\270\213\350\275\275/\346\246\202\345\277\265\345\233\276.png" differ diff --git "a/images/posts/2022-10-26-\345\211\215\347\253\257\345\205\245\351\227\250/class.jpg" "b/images/posts/2022-10-26-\345\211\215\347\253\257\345\205\245\351\227\250/class.jpg" new file mode 100644 index 0000000000..4d2b55f8c5 Binary files /dev/null and "b/images/posts/2022-10-26-\345\211\215\347\253\257\345\205\245\351\227\250/class.jpg" differ diff --git "a/images/posts/2022-10-26-\345\211\215\347\253\257\345\205\245\351\227\250/live Server.jpg" "b/images/posts/2022-10-26-\345\211\215\347\253\257\345\205\245\351\227\250/live Server.jpg" new file mode 100644 index 0000000000..93bf8ac3c5 Binary files /dev/null and "b/images/posts/2022-10-26-\345\211\215\347\253\257\345\205\245\351\227\250/live Server.jpg" differ diff --git "a/images/posts/2022-10-26-\345\211\215\347\253\257\345\205\245\351\227\250/\345\274\200\345\244\264\346\250\241\346\235\277.jpg" "b/images/posts/2022-10-26-\345\211\215\347\253\257\345\205\245\351\227\250/\345\274\200\345\244\264\346\250\241\346\235\277.jpg" new file mode 100644 index 0000000000..889a014820 Binary files /dev/null and "b/images/posts/2022-10-26-\345\211\215\347\253\257\345\205\245\351\227\250/\345\274\200\345\244\264\346\250\241\346\235\277.jpg" differ diff --git "a/images/posts/2022-12-14-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2501_\347\272\277\346\200\247\344\273\243\346\225\260/note_23_0.png" "b/images/posts/2022-12-14-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2501_\347\272\277\346\200\247\344\273\243\346\225\260/note_23_0.png" new file mode 100644 index 0000000000..8be3464207 Binary files /dev/null and "b/images/posts/2022-12-14-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2501_\347\272\277\346\200\247\344\273\243\346\225\260/note_23_0.png" differ diff --git "a/images/posts/2022-12-14-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2501_\347\272\277\346\200\247\344\273\243\346\225\260/note_25_0.png" "b/images/posts/2022-12-14-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2501_\347\272\277\346\200\247\344\273\243\346\225\260/note_25_0.png" new file mode 100644 index 0000000000..3ae1ded5be Binary files /dev/null and "b/images/posts/2022-12-14-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2501_\347\272\277\346\200\247\344\273\243\346\225\260/note_25_0.png" differ diff --git "a/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_10_1.png" "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_10_1.png" new file mode 100644 index 0000000000..959705daa2 Binary files /dev/null and "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_10_1.png" differ diff --git "a/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_11_0.png" "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_11_0.png" new file mode 100644 index 0000000000..70eb44ba32 Binary files /dev/null and "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_11_0.png" differ diff --git "a/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_13_0.png" "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_13_0.png" new file mode 100644 index 0000000000..fdee2dfb13 Binary files /dev/null and "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_13_0.png" differ diff --git "a/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_15_1.png" "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_15_1.png" new file mode 100644 index 0000000000..2f2a96ad4c Binary files /dev/null and "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_15_1.png" differ diff --git "a/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_17_1.png" "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_17_1.png" new file mode 100644 index 0000000000..4f65668056 Binary files /dev/null and "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_17_1.png" differ diff --git "a/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_19_1.png" "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_19_1.png" new file mode 100644 index 0000000000..2408dcbad7 Binary files /dev/null and "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_19_1.png" differ diff --git "a/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_21_1.png" "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_21_1.png" new file mode 100644 index 0000000000..2e0869d3d9 Binary files /dev/null and "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_21_1.png" differ diff --git "a/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_26_1.png" "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_26_1.png" new file mode 100644 index 0000000000..139c15a671 Binary files /dev/null and "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_26_1.png" differ diff --git "a/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_28_1.png" "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_28_1.png" new file mode 100644 index 0000000000..4f8cc8b057 Binary files /dev/null and "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_28_1.png" differ diff --git "a/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_30_2.png" "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_30_2.png" new file mode 100644 index 0000000000..93fc002c62 Binary files /dev/null and "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_30_2.png" differ diff --git "a/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_4_1.png" "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_4_1.png" new file mode 100644 index 0000000000..dbdb2c6465 Binary files /dev/null and "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/output_4_1.png" differ diff --git "a/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/\346\234\211\345\220\221\345\233\276\347\244\272\344\276\213.jpg" "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/\346\234\211\345\220\221\345\233\276\347\244\272\344\276\213.jpg" new file mode 100644 index 0000000000..90a4e169d7 Binary files /dev/null and "b/images/posts/2022-12-16-\346\234\272\345\231\250\345\255\246\344\271\240\345\205\245\351\227\2502_\346\246\202\347\216\207\350\256\272/\346\234\211\345\220\221\345\233\276\347\244\272\344\276\213.jpg" differ diff --git "a/images/posts/2022-12-28-pytest\346\265\213\350\257\225\345\256\236\346\210\230/Task\351\241\271\347\233\256\346\226\207\344\273\266\345\244\271\347\233\256\345\275\225.jpg" "b/images/posts/2022-12-28-pytest\346\265\213\350\257\225\345\256\236\346\210\230/Task\351\241\271\347\233\256\346\226\207\344\273\266\345\244\271\347\233\256\345\275\225.jpg" new file mode 100644 index 0000000000..7e54e96b8f Binary files /dev/null and "b/images/posts/2022-12-28-pytest\346\265\213\350\257\225\345\256\236\346\210\230/Task\351\241\271\347\233\256\346\226\207\344\273\266\345\244\271\347\233\256\345\275\225.jpg" differ diff --git "a/images/posts/2023-01-11-Linux\347\275\221\347\273\234\351\205\215\347\275\256/ifcfg\345\270\270\350\247\201\351\205\215\347\275\256.jpg" "b/images/posts/2023-01-11-Linux\347\275\221\347\273\234\351\205\215\347\275\256/ifcfg\345\270\270\350\247\201\351\205\215\347\275\256.jpg" new file mode 100644 index 0000000000..2862763040 Binary files /dev/null and "b/images/posts/2023-01-11-Linux\347\275\221\347\273\234\351\205\215\347\275\256/ifcfg\345\270\270\350\247\201\351\205\215\347\275\256.jpg" differ diff --git "a/images/posts/2023-01-11-Linux\347\275\221\347\273\234\351\205\215\347\275\256/ifconfig\345\221\275\344\273\244\347\232\204\344\270\273\350\246\201\345\217\202\346\225\260.jpg" "b/images/posts/2023-01-11-Linux\347\275\221\347\273\234\351\205\215\347\275\256/ifconfig\345\221\275\344\273\244\347\232\204\344\270\273\350\246\201\345\217\202\346\225\260.jpg" new file mode 100644 index 0000000000..93951bedfa Binary files /dev/null and "b/images/posts/2023-01-11-Linux\347\275\221\347\273\234\351\205\215\347\275\256/ifconfig\345\221\275\344\273\244\347\232\204\344\270\273\350\246\201\345\217\202\346\225\260.jpg" differ diff --git "a/images/posts/2023-01-11-Linux\347\275\221\347\273\234\351\205\215\347\275\256/ping\345\221\275\344\273\244\345\270\270\347\224\250\345\217\202\346\225\260.jpg" "b/images/posts/2023-01-11-Linux\347\275\221\347\273\234\351\205\215\347\275\256/ping\345\221\275\344\273\244\345\270\270\347\224\250\345\217\202\346\225\260.jpg" new file mode 100644 index 0000000000..3c3c31d3d7 Binary files /dev/null and "b/images/posts/2023-01-11-Linux\347\275\221\347\273\234\351\205\215\347\275\256/ping\345\221\275\344\273\244\345\270\270\347\224\250\345\217\202\346\225\260.jpg" differ diff --git "a/images/posts/2023-01-16-\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\344\271\213requests/\346\216\245\345\217\243\347\224\250\344\276\213\347\274\226\345\206\231\344\276\213\345\255\220.jpg" "b/images/posts/2023-01-16-\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\344\271\213requests/\346\216\245\345\217\243\347\224\250\344\276\213\347\274\226\345\206\231\344\276\213\345\255\220.jpg" new file mode 100644 index 0000000000..05f8d4c6e2 Binary files /dev/null and "b/images/posts/2023-01-16-\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\344\271\213requests/\346\216\245\345\217\243\347\224\250\344\276\213\347\274\226\345\206\231\344\276\213\345\255\220.jpg" differ diff --git "a/images/posts/2023-01-17-Python\344\270\255\345\206\205\345\255\230\345\222\214\345\217\230\351\207\217\347\256\241\347\220\206/\344\270\215\345\217\257\345\217\230\345\257\271\350\261\241.png" "b/images/posts/2023-01-17-Python\344\270\255\345\206\205\345\255\230\345\222\214\345\217\230\351\207\217\347\256\241\347\220\206/\344\270\215\345\217\257\345\217\230\345\257\271\350\261\241.png" new file mode 100644 index 0000000000..e4c763476f Binary files /dev/null and "b/images/posts/2023-01-17-Python\344\270\255\345\206\205\345\255\230\345\222\214\345\217\230\351\207\217\347\256\241\347\220\206/\344\270\215\345\217\257\345\217\230\345\257\271\350\261\241.png" differ diff --git "a/images/posts/2023-01-17-Python\344\270\255\345\206\205\345\255\230\345\222\214\345\217\230\351\207\217\347\256\241\347\220\206/\345\217\257\345\217\230\345\257\271\350\261\241.png" "b/images/posts/2023-01-17-Python\344\270\255\345\206\205\345\255\230\345\222\214\345\217\230\351\207\217\347\256\241\347\220\206/\345\217\257\345\217\230\345\257\271\350\261\241.png" new file mode 100644 index 0000000000..9ac69fecbb Binary files /dev/null and "b/images/posts/2023-01-17-Python\344\270\255\345\206\205\345\255\230\345\222\214\345\217\230\351\207\217\347\256\241\347\220\206/\345\217\257\345\217\230\345\257\271\350\261\241.png" differ diff --git "a/images/posts/2023-01-17-Python\344\270\255\345\206\205\345\255\230\345\222\214\345\217\230\351\207\217\347\256\241\347\220\206/\345\274\225\347\224\250\344\270\216\345\257\271\350\261\241.jpg" "b/images/posts/2023-01-17-Python\344\270\255\345\206\205\345\255\230\345\222\214\345\217\230\351\207\217\347\256\241\347\220\206/\345\274\225\347\224\250\344\270\216\345\257\271\350\261\241.jpg" new file mode 100644 index 0000000000..bc3928e4cb Binary files /dev/null and "b/images/posts/2023-01-17-Python\344\270\255\345\206\205\345\255\230\345\222\214\345\217\230\351\207\217\347\256\241\347\220\206/\345\274\225\347\224\250\344\270\216\345\257\271\350\261\241.jpg" differ diff --git "a/images/posts/2023-01-19-\350\275\257\344\273\266\346\265\213\350\257\225\347\232\204\345\210\206\347\261\273/\350\275\257\344\273\266\346\265\213\350\257\225\347\232\204\345\210\206\347\261\273.png" "b/images/posts/2023-01-19-\350\275\257\344\273\266\346\265\213\350\257\225\347\232\204\345\210\206\347\261\273/\350\275\257\344\273\266\346\265\213\350\257\225\347\232\204\345\210\206\347\261\273.png" new file mode 100644 index 0000000000..e2fa4ea16f Binary files /dev/null and "b/images/posts/2023-01-19-\350\275\257\344\273\266\346\265\213\350\257\225\347\232\204\345\210\206\347\261\273/\350\275\257\344\273\266\346\265\213\350\257\225\347\232\204\345\210\206\347\261\273.png" differ diff --git "a/images/posts/2023-01-29-\346\225\260\346\215\256\351\251\261\345\212\250\347\232\204\350\207\252\345\212\250\345\214\226\346\265\213\350\257\2251/Excel\347\224\250\344\276\213\350\256\276\350\256\241.png" "b/images/posts/2023-01-29-\346\225\260\346\215\256\351\251\261\345\212\250\347\232\204\350\207\252\345\212\250\345\214\226\346\265\213\350\257\2251/Excel\347\224\250\344\276\213\350\256\276\350\256\241.png" new file mode 100644 index 0000000000..426ef270e6 Binary files /dev/null and "b/images/posts/2023-01-29-\346\225\260\346\215\256\351\251\261\345\212\250\347\232\204\350\207\252\345\212\250\345\214\226\346\265\213\350\257\2251/Excel\347\224\250\344\276\213\350\256\276\350\256\241.png" differ diff --git "a/images/posts/2023-01-30-\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\344\273\245\345\217\212\346\265\213\350\257\225\347\224\250\344\276\213\350\256\276\350\256\241\346\200\273\347\273\223/\347\224\250\346\210\267\347\231\273\345\275\225\346\265\201\347\250\213\345\233\2762.png" "b/images/posts/2023-01-30-\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\344\273\245\345\217\212\346\265\213\350\257\225\347\224\250\344\276\213\350\256\276\350\256\241\346\200\273\347\273\223/\347\224\250\346\210\267\347\231\273\345\275\225\346\265\201\347\250\213\345\233\2762.png" new file mode 100644 index 0000000000..c9d3407f68 Binary files /dev/null and "b/images/posts/2023-01-30-\346\216\245\345\217\243\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\344\273\245\345\217\212\346\265\213\350\257\225\347\224\250\344\276\213\350\256\276\350\256\241\346\200\273\347\273\223/\347\224\250\346\210\267\347\231\273\345\275\225\346\265\201\347\250\213\345\233\2762.png" differ diff --git a/images/posts/Wifilist/PastedGraphic.png b/images/posts/Wifilist/PastedGraphic.png deleted file mode 100644 index b5ec853fd8..0000000000 Binary files a/images/posts/Wifilist/PastedGraphic.png and /dev/null differ diff --git a/images/posts/Wifilist/WLAN.png b/images/posts/Wifilist/WLAN.png deleted file mode 100644 index 282e9399ef..0000000000 Binary files a/images/posts/Wifilist/WLAN.png and /dev/null differ diff --git a/images/posts/Wifilist/entitlement.png b/images/posts/Wifilist/entitlement.png deleted file mode 100644 index 52c8204111..0000000000 Binary files a/images/posts/Wifilist/entitlement.png and /dev/null differ diff --git a/images/posts/Wifilist/infoplist.png b/images/posts/Wifilist/infoplist.png deleted file mode 100644 index 2cbc879893..0000000000 Binary files a/images/posts/Wifilist/infoplist.png and /dev/null differ diff --git a/images/posts/Wifilist/systemVersion.png b/images/posts/Wifilist/systemVersion.png deleted file mode 100644 index 46361a8d36..0000000000 Binary files a/images/posts/Wifilist/systemVersion.png and /dev/null differ diff --git a/images/posts/Xcode8/image1.png b/images/posts/Xcode8/image1.png deleted file mode 100644 index 4617c0b1d1..0000000000 Binary files a/images/posts/Xcode8/image1.png and /dev/null differ diff --git a/images/posts/Xcode8/image10.png b/images/posts/Xcode8/image10.png deleted file mode 100644 index 921c368b95..0000000000 Binary files a/images/posts/Xcode8/image10.png and /dev/null differ diff --git a/images/posts/Xcode8/image11.png b/images/posts/Xcode8/image11.png deleted file mode 100644 index a570a7d6fe..0000000000 Binary files a/images/posts/Xcode8/image11.png and /dev/null differ diff --git a/images/posts/Xcode8/image12.png b/images/posts/Xcode8/image12.png deleted file mode 100644 index e9a02f272d..0000000000 Binary files a/images/posts/Xcode8/image12.png and /dev/null differ diff --git a/images/posts/Xcode8/image13.png b/images/posts/Xcode8/image13.png deleted file mode 100644 index be5bcd798d..0000000000 Binary files a/images/posts/Xcode8/image13.png and /dev/null differ diff --git a/images/posts/Xcode8/image2.png b/images/posts/Xcode8/image2.png deleted file mode 100644 index a557cdaf4e..0000000000 Binary files a/images/posts/Xcode8/image2.png and /dev/null differ diff --git a/images/posts/Xcode8/image3.png b/images/posts/Xcode8/image3.png deleted file mode 100644 index 416ea41d98..0000000000 Binary files a/images/posts/Xcode8/image3.png and /dev/null differ diff --git a/images/posts/Xcode8/image4.png b/images/posts/Xcode8/image4.png deleted file mode 100644 index 219ee6204b..0000000000 Binary files a/images/posts/Xcode8/image4.png and /dev/null differ diff --git a/images/posts/Xcode8/image5.png b/images/posts/Xcode8/image5.png deleted file mode 100644 index 6c53085795..0000000000 Binary files a/images/posts/Xcode8/image5.png and /dev/null differ diff --git a/images/posts/Xcode8/image6.png b/images/posts/Xcode8/image6.png deleted file mode 100644 index 56b6662805..0000000000 Binary files a/images/posts/Xcode8/image6.png and /dev/null differ diff --git a/images/posts/Xcode8/image7.png b/images/posts/Xcode8/image7.png deleted file mode 100644 index 46741143f4..0000000000 Binary files a/images/posts/Xcode8/image7.png and /dev/null differ diff --git a/images/posts/Xcode8/image8.png b/images/posts/Xcode8/image8.png deleted file mode 100644 index ee6900a05a..0000000000 Binary files a/images/posts/Xcode8/image8.png and /dev/null differ diff --git a/images/posts/Xcode8/image9.png b/images/posts/Xcode8/image9.png deleted file mode 100644 index 45949c1788..0000000000 Binary files a/images/posts/Xcode8/image9.png and /dev/null differ diff --git a/images/posts/codeless/code.png b/images/posts/codeless/code.png deleted file mode 100644 index 61da6c1972..0000000000 Binary files a/images/posts/codeless/code.png and /dev/null differ diff --git a/images/posts/codeless/codeless1.png b/images/posts/codeless/codeless1.png deleted file mode 100644 index 36f08816a8..0000000000 Binary files a/images/posts/codeless/codeless1.png and /dev/null differ diff --git a/images/posts/codeless/codeless2.png b/images/posts/codeless/codeless2.png deleted file mode 100644 index 5797b9b669..0000000000 Binary files a/images/posts/codeless/codeless2.png and /dev/null differ diff --git a/images/posts/codeless/image01.png b/images/posts/codeless/image01.png deleted file mode 100644 index 5d3a60b31b..0000000000 Binary files a/images/posts/codeless/image01.png and /dev/null differ diff --git a/images/posts/codeless/image02.png b/images/posts/codeless/image02.png deleted file mode 100644 index 7bcf64fa57..0000000000 Binary files a/images/posts/codeless/image02.png and /dev/null differ diff --git a/images/posts/codeless/image03.png b/images/posts/codeless/image03.png deleted file mode 100644 index 2dce89fd87..0000000000 Binary files a/images/posts/codeless/image03.png and /dev/null differ diff --git a/images/posts/codeless/image04.png b/images/posts/codeless/image04.png deleted file mode 100644 index d3985d8a2e..0000000000 Binary files a/images/posts/codeless/image04.png and /dev/null differ diff --git a/images/posts/codeless/image05.png b/images/posts/codeless/image05.png deleted file mode 100644 index 028c616220..0000000000 Binary files a/images/posts/codeless/image05.png and /dev/null differ diff --git a/images/posts/codeless/img1.png b/images/posts/codeless/img1.png deleted file mode 100644 index 6400699c20..0000000000 Binary files a/images/posts/codeless/img1.png and /dev/null differ diff --git a/images/posts/codeless/img2.png b/images/posts/codeless/img2.png deleted file mode 100644 index b3933ae01c..0000000000 Binary files a/images/posts/codeless/img2.png and /dev/null differ diff --git a/images/posts/iBeacon/iBeacon.png b/images/posts/iBeacon/iBeacon.png deleted file mode 100755 index bb4362e223..0000000000 Binary files a/images/posts/iBeacon/iBeacon.png and /dev/null differ diff --git a/images/posts/icon/image01.png b/images/posts/icon/image01.png deleted file mode 100644 index 19c66e2f06..0000000000 Binary files a/images/posts/icon/image01.png and /dev/null differ diff --git a/images/posts/jekyll/image1.png b/images/posts/jekyll/image1.png deleted file mode 100644 index c7cffb9ddb..0000000000 Binary files a/images/posts/jekyll/image1.png and /dev/null differ diff --git a/images/posts/markdown/image1.png b/images/posts/markdown/image1.png deleted file mode 100644 index 654615f0f9..0000000000 Binary files a/images/posts/markdown/image1.png and /dev/null differ diff --git a/images/posts/markdown/image2.png b/images/posts/markdown/image2.png deleted file mode 100644 index 5479feecb7..0000000000 Binary files a/images/posts/markdown/image2.png and /dev/null differ diff --git a/images/posts/markdown/image3.png b/images/posts/markdown/image3.png deleted file mode 100644 index 060069ff35..0000000000 Binary files a/images/posts/markdown/image3.png and /dev/null differ diff --git a/images/posts/tfimg/ck.jpg b/images/posts/tfimg/ck.jpg deleted file mode 100644 index 355cfcdf92..0000000000 Binary files a/images/posts/tfimg/ck.jpg and /dev/null differ diff --git a/images/posts/tfimg/image01.png b/images/posts/tfimg/image01.png deleted file mode 100644 index 906da7ea29..0000000000 Binary files a/images/posts/tfimg/image01.png and /dev/null differ diff --git a/images/posts/tfimg/image02.png b/images/posts/tfimg/image02.png deleted file mode 100644 index bdb6385cae..0000000000 Binary files a/images/posts/tfimg/image02.png and /dev/null differ diff --git a/images/posts/tfimg/ipod1.jpg b/images/posts/tfimg/ipod1.jpg deleted file mode 100644 index e0865e8272..0000000000 Binary files a/images/posts/tfimg/ipod1.jpg and /dev/null differ diff --git a/images/posts/tfimg/ipod2.jpg b/images/posts/tfimg/ipod2.jpg deleted file mode 100644 index 7af0682836..0000000000 Binary files a/images/posts/tfimg/ipod2.jpg and /dev/null differ diff --git a/images/posts/tfimg/logo.jpg b/images/posts/tfimg/logo.jpg deleted file mode 100644 index d5005c10ad..0000000000 Binary files a/images/posts/tfimg/logo.jpg and /dev/null differ diff --git a/images/posts/tfimg/mouse1.jpg b/images/posts/tfimg/mouse1.jpg deleted file mode 100644 index 7c0ad42346..0000000000 Binary files a/images/posts/tfimg/mouse1.jpg and /dev/null differ diff --git a/images/posts/tfimg/mouse2.jpg b/images/posts/tfimg/mouse2.jpg deleted file mode 100644 index 6cf07069da..0000000000 Binary files a/images/posts/tfimg/mouse2.jpg and /dev/null differ diff --git a/images/posts/tfimg/notebook.jpg b/images/posts/tfimg/notebook.jpg deleted file mode 100644 index 0ef56d6e58..0000000000 Binary files a/images/posts/tfimg/notebook.jpg and /dev/null differ diff --git a/images/posts/tfimg/wb.jpg b/images/posts/tfimg/wb.jpg deleted file mode 100644 index a3b97a1dc1..0000000000 Binary files a/images/posts/tfimg/wb.jpg and /dev/null differ diff --git a/images/posts/tfimg/wq.jpg b/images/posts/tfimg/wq.jpg deleted file mode 100644 index 58c311ed9b..0000000000 Binary files a/images/posts/tfimg/wq.jpg and /dev/null differ diff --git a/images/readme/icon.gif b/images/readme/icon.gif deleted file mode 100644 index d916237dc4..0000000000 Binary files a/images/readme/icon.gif and /dev/null differ diff --git a/images/readme/img1.png b/images/readme/img1.png deleted file mode 100644 index ee6d289ac1..0000000000 Binary files a/images/readme/img1.png and /dev/null differ diff --git a/images/readme/img2.png b/images/readme/img2.png deleted file mode 100644 index b8a9047495..0000000000 Binary files a/images/readme/img2.png and /dev/null differ diff --git a/images/readme/img3.png b/images/readme/img3.png deleted file mode 100644 index c4d5670ceb..0000000000 Binary files a/images/readme/img3.png and /dev/null differ diff --git a/images/readme/img4.png b/images/readme/img4.png deleted file mode 100644 index 3ad19d6c35..0000000000 Binary files a/images/readme/img4.png and /dev/null differ diff --git a/images/readme/img5.png b/images/readme/img5.png deleted file mode 100644 index 8704ad959a..0000000000 Binary files a/images/readme/img5.png and /dev/null differ diff --git a/support.md b/support.md index fd4d025058..072b0c0409 100755 --- a/support.md +++ b/support.md @@ -3,54 +3,6 @@ layout: page title: 技术支持 --- -博客源码在 Github 上,你的 Star 是我更新的动力,谢谢~ +## 联系方式 -遇到问题请先自行排查,可以直接在[Issues](https://github.com/leopardpan/leopardpan.github.io/issues)里面提问,不过回复的可能不及时。 - -目前我已经给很多小伙伴做过单独的技术支持了,现在设置 `付费支持功能` - -

付费打赏技术支持

- -如果你付费了,请邮件联系我,把付费的截图和你的QQ号一起发给我, 感谢你的赞助。 - -你向我提问题我也要花时间帮你解答,付费多少看你自己的心意,`10、20块不嫌少`,`100、200块不嫌多`。 - -**注:** 付款后概不退款,拒绝扯皮,我的博客模板一直都是免费给大家使用。 - - -

我能给你解答的问题有

- -* Hexo 、Jekyll 搭建个人博客遇到的问题技术支持 -* 域名申请,DNS配置遇到的问题技术支持 -* 自建服务器部署个人博客,服务器配置 -* Nginx 反向代理、supervisor 守护进程 -* 域名备案、SSL证书配置 - -我可能不是所有的问题都能解答,但是我会带着你去学习,一起成长! - -

我是一个iOS开发者

- -其实我是一个正正经经的iOS开发者, 并且一直在做 iOS 开发6年了。 - -相对于搭建博客的问题,我iOS 技术会更专业。 - -如果有关于iOS的问题我们也可以一起讨论 - - -

感谢您的打赏

- -![](/images/payimg/alipayimg.jpg) - -![](/images/payimg/weipayimg.jpg) - -

如何联系到我

- -

-email:leoparpan@icloud.com -

-qq: 1499065178 -

- -{% include comments.html %} -