Ghi nhật ký là một cách để lưu trữ thông tin về tập lệnh của bạn và theo dõi các sự kiện xảy ra. Khi viết bất kỳ tập lệnh phức tạp nào bằng Python, việc ghi nhật ký là điều cần thiết để gỡ lỗi phần mềm khi bạn phát triển phần mềm đó. Nếu không đăng nhập, việc tìm nguồn gốc của sự cố trong mã của bạn có thể cực kỳ tốn thời gian
Sau khi hoàn thành hướng dẫn này, bạn sẽ biết
- Tại sao chúng tôi muốn sử dụng mô-đun ghi nhật ký
- Cách sử dụng mô-đun ghi nhật ký
- Cách tùy chỉnh cơ chế ghi nhật ký
Bắt đầu dự án của bạn với cuốn sách mới Python for Machine Learning của tôi, bao gồm các hướng dẫn từng bước và các tệp mã nguồn Python cho tất cả các ví dụ
Bắt đầu nàoLogging in Python
Photo by ilaria88. Some rights reserved
Tutorial Overview
This tutorial is divided into four parts; they are
- The benefits of logging
- Basic logging
- Advanced configuration to logging
- An example of the use of logging
Benefits of Logging
You may ask. “Why not just use printing?”
When you run an algorithm and want to confirm it is doing what you expected, it is natural to add some print[]
statements at strategic locations to show the program’s state. Printing can help debug simpler scripts, but as your code gets more and more complex, printing lacks the flexibility and robustness that logging has
With logging, you can pinpoint where a logging call came from, differentiate severity between messages, and write information to a file, which printing cannot do. For example, we can turn on and off the message from a particular module of a larger program. We can also increase or decrease the verbosity of the logging messages without changing a lot of code
Basic Logging
Python has a built-in library, logging,
for this purpose. It is simple to create a “logger” to log messages or information that you would like to see
The logging system in Python operates under a hierarchical namespace and different levels of severity. The Python script can create a logger under a namespace, and every time a message is logged, the script must specify its severity. The logged message can go to different places depending on the handler we set up for the namespace. The most common handler is to simply print on the screen, like the ubiquitous print[]
function. When we start the program, we may register a new handler and set up the level of severity to which the handler will react
There are 5 different logging levels that indicate the severity of the logs, shown in increasing severity
- DEBUG
- INFO
- WARNING
- ERROR
- CRITICAL
A very simple example of logging is shown below, using the default logger or the root logger
1
2
3
4
5
6
7
import logging
logging. debug['Debug message']
logging. info['Info message']
logging. warning['Warning message']
logging. error['Error message']
logging. critical['Critical message']
These will emit log messages of different severity. While there are five lines of logging, you may see only three lines of output if you run this script, as follows
1
2
3
CẢNH BÁO. root. This is a warning message
ERROR. root. This is an error message
CRITICAL. root. This is a critical message
This is because the root logger, by default, only prints the log messages of a severity level of WARNING or above. However, using the root logger this way is not much different from using the print[] function
The settings for the root logger are not set in stone. We can configure the root logger to output to a particular file, change its default severity level, and format the output. Here’s an example
1
2
3
4
5
6
7
8
9
10
11
import logging
logging. basicConfig[filename = 'file. log',
level = logging. DEBUG,
format = '%[asctime]s. %[levelname]s. %[name]s. %[message]s']
logging. debug['Debug message']
logging. info['Info message']
logging. warning['Warning message']
logging. error['Error message']
logging. critical['Critical message']
Running this script will produce no output to the screen but will have the following in the newly created file file.log
1
2
3
4
5
2022-03-22 20. 41. 08,151. DEBUG. root. Debug message
2022-03-22 20. 41. 08,152. INFO. root. Info message
2022-03-22 20. 41. 08,152. WARNING. nguồn gốc. Warning message
2022-03-22 20. 41. 08,152. LỖI. nguồn gốc. Thông báo lỗi
2022-03-22 20. 41. 08,152. BẠO KÍCH. nguồn gốc. thông điệp quan trọng
Lệnh gọi tới logging.basicConfig[]
là thay đổi trình ghi nhật ký gốc. Trong ví dụ của chúng tôi, chúng tôi đặt trình xử lý xuất thành tệp thay vì màn hình, điều chỉnh cấp độ ghi nhật ký sao cho tất cả thông báo tường trình ở cấp độ GỠ LỖI hoặc cao hơn đều được xử lý, đồng thời thay đổi định dạng của đầu ra thông báo tường trình để bao gồm thời gian
Lưu ý rằng bây giờ tất cả năm thông báo đã được xuất ra, do đó, mức mặc định mà bộ ghi nhật ký gốc hiện là “DEBUG. ” Có thể tìm thấy các thuộc tính bản ghi nhật ký [chẳng hạn như %[asctime]s
] được sử dụng để định dạng đầu ra
Mặc dù có một trình ghi nhật ký mặc định nhưng chúng tôi thường muốn tạo và sử dụng các trình ghi nhật ký khác có thể được cấu hình riêng. Điều này là do chúng tôi có thể muốn một mức độ nghiêm trọng hoặc định dạng khác nhau cho các trình ghi nhật ký khác nhau. Một logger mới có thể được tạo ra với
1
ghi nhật ký = ghi nhật ký. getLogger["logger_name"]
Trong nội bộ, các logger được tổ chức theo thứ bậc. Một logger được tạo bằng
1
ghi nhật ký = ghi nhật ký. getLogger["parent. con"]
sẽ là một trình ghi nhật ký con được tạo bên dưới trình ghi nhật ký có tên “parent
,” đến lượt nó, nằm dưới trình ghi nhật ký gốc. Sử dụng dấu chấm trong chuỗi biểu thị rằng trình ghi con là con của trình ghi gốc. Trong trường hợp trên, một bộ ghi có tên “_______7_______” được tạo cũng như một bộ ghi có tên "parent"
ngầm định
Khi tạo, một bộ ghi con có tất cả các thuộc tính của bộ ghi cha cho đến khi được cấu hình lại. Chúng ta có thể chứng minh điều này với ví dụ sau
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import logging
# Tạo `cha mẹ. con` logger
ghi nhật ký = ghi nhật ký. getLogger["parent. con"]
# Phát ra thông báo tường trình ở mức INFO, theo mặc định, thông báo này không được in ra màn hình
nhật ký. thông tin["đây là cấp độ thông tin"]
# Tạo logger `cha mẹ`
parentlogger = ghi nhật ký. getLogger["parent"]
# Đặt cấp độ của cha mẹ thành INFO và chỉ định trình xử lý mới
trình xử lý = ghi nhật ký. StreamHandler[]
trình xử lý. setFormatter[ghi nhật ký. Trình định dạng["%[asctime]s. %[name]s. %[tên cấp độ]s. %[tin nhắn]s"]]
parentlogger. setLevel[ghi nhật ký. THÔNG TIN]
parentlogger. addHandler[trình xử lý]
# Để bộ ghi nhật ký con gửi lại thông điệp nhật ký
nhật ký. thông tin["đây lại là cấp độ thông tin"]
Đoạn mã này sẽ chỉ xuất ra một dòng
1
2022-03-28 19:23. 29,315. cha mẹ. con. THÔNG TIN. đây là thông tin cấp lại
được tạo bởi đối tượng StreamHandler với chuỗi định dạng tùy chỉnh. Điều này chỉ xảy ra sau khi chúng tôi định cấu hình lại trình ghi nhật ký cho parent
vì nếu không, cấu hình của trình ghi gốc sẽ chiếm ưu thế và không có thông báo nào ở cấp INFO sẽ được in
Cấu hình nâng cao để ghi nhật ký
Như chúng ta đã thấy trong ví dụ trước, chúng ta có thể định cấu hình bộ ghi mà chúng ta đã tạo
Ngưỡng cấp độ
Giống như cấu hình cơ bản của bộ ghi gốc, chúng tôi cũng có thể định cấu hình đích đầu ra, mức độ nghiêm trọng và định dạng của bộ ghi. Sau đây là cách chúng tôi có thể đặt ngưỡng cấp độ của bộ ghi thành INFO
1
2
parent_logger = ghi nhật ký. getLogger["parent"]
parent_logger. setLevel[ghi nhật ký. THÔNG TIN]
Giờ đây, các lệnh có mức độ nghiêm trọng INFO trở lên sẽ được ghi lại bởi parent_logger. Nhưng nếu đây là tất cả những gì bạn đã làm, bạn sẽ không thấy bất cứ điều gì từ logging,
0 bởi vì không có trình xử lý nào được chỉ định cho trình ghi nhật ký này. Trên thực tế, cũng không có trình xử lý nào cho trình ghi nhật ký gốc trừ khi bạn thiết lập một trình xử lý với logging.basicConfig[]
Trình xử lý nhật ký
Chúng tôi có thể định cấu hình đích đầu ra của trình ghi nhật ký của mình bằng các trình xử lý. Trình xử lý chịu trách nhiệm gửi thông điệp tường trình đến đúng đích. Có một số loại trình xử lý; . Với logging,
2, bộ ghi sẽ xuất ra thiết bị đầu cuối, trong khi với logging,
3, bộ ghi sẽ xuất ra một tệp cụ thể
Đây là một ví dụ về việc sử dụng logging,
2 để xuất nhật ký ra thiết bị đầu cuối
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import logging
# Thiết lập bộ ghi gốc và thêm trình xử lý tệp vào bộ ghi gốc
logging. basicConfig[filename = 'file. log',
cấp độ = ghi nhật ký. CẢNH BÁO,
format = '%[asctime]s. %[levelname]s. %[name]s. %[message]s']
# Tạo trình ghi nhật ký, đặt cấp độ và thêm trình xử lý luồng
parent_logger = ghi nhật ký. getLogger["parent"]
parent_logger. setLevel[ghi nhật ký. THÔNG TIN]
parent_shandler = ghi nhật ký. StreamHandler[]
parent_logger. addHandler[parent_shandler]
# Thông báo nhật ký nghiêm trọng INFO hoặc cao hơn sẽ được xử lý
parent_logger. gỡ lỗi['Thông báo gỡ lỗi']
parent_logger. thông tin['Thông tin thông báo']
parent_logger. cảnh báo['Thông báo cảnh báo']
parent_logger. lỗi['Thông báo lỗi']
parent_logger. quan trọng['Thông báo quan trọng']
Trong đoạn mã trên, có hai trình xử lý được tạo. Một logging,
3 được tạo bởi logging.basicConfig[]
cho trình ghi gốc và một logging,
2 được tạo cho trình ghi nhật ký parent
Lưu ý rằng mặc dù có một logging,
2 gửi nhật ký đến thiết bị đầu cuối, các nhật ký từ bộ ghi nhật ký parent
vẫn được gửi đến file.log
vì nó là con của bộ ghi nhật ký gốc và trình xử lý của bộ ghi nhật ký gốc cũng đang hoạt động đối với các thông điệp nhật ký của đứa trẻ. Chúng ta có thể thấy rằng các bản ghi vào thiết bị đầu cuối bao gồm các thông báo cấp INFO trở lên
1
2
3
4
tin nhắn thông tin
Tin nhắn cảnh báo
Thông báo lỗi
thông điệp quan trọng
Nhưng đầu ra của thiết bị đầu cuối không được định dạng, vì chúng tôi chưa sử dụng print[]
4. Nhật ký của file.log
, tuy nhiên, đã thiết lập print[]
4 và nó sẽ giống như sau
1
2
3
4
2022-03-22 23. 07. 12,533. THÔNG TIN. cha mẹ. tin nhắn thông tin
2022-03-22 23. 07. 12,533. CẢNH BÁO. cha mẹ. Tin nhắn cảnh báo
2022-03-22 23. 07. 12,533. LỖI. cha mẹ. Thông báo lỗi
2022-03-22 23. 07. 12,533. BẠO KÍCH. cha mẹ. thông điệp quan trọng
Ngoài ra, chúng ta có thể sử dụng logging,
3 trong ví dụ trên của print[]
8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import logging
# Thiết lập bộ ghi gốc và thêm trình xử lý tệp vào bộ ghi gốc
logging. basicConfig[filename = 'file. log',
cấp độ = ghi nhật ký. CẢNH BÁO,
format = '%[asctime]s. %[levelname]s. %[name]s. %[message]s']
# Tạo trình ghi nhật ký, đặt cấp độ và thêm trình xử lý luồng
parent_logger = ghi nhật ký. getLogger["parent"]
parent_logger. setLevel[ghi nhật ký. THÔNG TIN]
parent_fhandler = ghi nhật ký. Trình xử lý tệp['parent. log']
parent_fhandler. setLevel[ghi nhật ký. CẢNH BÁO]
parent_logger. addHandler[parent_fhandler]
# Thông báo nhật ký nghiêm trọng INFO hoặc cao hơn sẽ được xử lý
parent_logger. gỡ lỗi['Thông báo gỡ lỗi']
parent_logger. thông tin['Thông tin thông báo']
parent_logger. cảnh báo['Thông báo cảnh báo']
parent_logger. lỗi['Thông báo lỗi']
parent_logger. quan trọng['Thông báo quan trọng']
Ví dụ trên đã chứng minh rằng bạn cũng có thể đặt cấp độ của trình xử lý. Cấp độ print[]
9 lọc ra các nhật ký không ở cấp độ CẢNH BÁO hoặc cao hơn. Tuy nhiên, nếu bạn đặt cấp độ của trình xử lý thành GỠ LỖI, thì điều đó cũng giống như không đặt cấp độ vì nhật ký GỠ LỖI đã được lọc ra theo cấp độ của trình ghi nhật ký, đó là THÔNG TIN
Trong trường hợp này, đầu ra của file.log
0 là
1
2
3
Tin nhắn cảnh báo
Thông báo lỗi
thông điệp quan trọng
trong khi đó của file.log
vẫn giống như trước đây. Tóm lại, khi một thông điệp nhật ký được ghi lại bởi một bộ ghi nhật ký,
- Cấp độ của trình ghi nhật ký sẽ xác định xem thông báo có đủ nghiêm trọng để xử lý hay không. Nếu cấp độ của trình ghi nhật ký không được đặt, thì cấp độ của trình ghi gốc [và cuối cùng là trình ghi nhật ký gốc] sẽ được sử dụng cho việc xem xét này
- Nếu thông báo nhật ký sẽ được xử lý, thì tất cả các trình xử lý đã từng được thêm dọc theo hệ thống phân cấp bộ ghi nhật ký cho đến bộ ghi nhật ký gốc sẽ nhận được một bản sao của thông báo. Each handler’s level will determine if this particular handler should honor this message
Trình định dạng
Để cấu hình định dạng của logger, chúng tôi sử dụng một print[]
4. Nó cho phép chúng tôi đặt định dạng của nhật ký, tương tự như cách chúng tôi đã làm như vậy trong file.log
3 của trình ghi nhật ký gốc. Đây là cách chúng tôi có thể thêm trình định dạng vào trình xử lý của mình
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import logging
# Thiết lập bộ ghi gốc và thêm trình xử lý tệp vào bộ ghi gốc
logging. basicConfig[filename = 'file. log',
cấp độ = ghi nhật ký. CẢNH BÁO,
format = '%[asctime]s. %[levelname]s. %[name]s. %[message]s']
# Tạo trình ghi nhật ký, đặt cấp độ và thêm trình xử lý luồng
parent_logger = ghi nhật ký. getLogger["parent"]
parent_logger. setLevel[ghi nhật ký. THÔNG TIN]
parent_fhandler = ghi nhật ký. Trình xử lý tệp['parent. log']
parent_fhandler. setLevel[ghi nhật ký. CẢNH BÁO]
parent_formatter = ghi nhật ký. Trình định dạng['%[asctime]s. %[tên cấp độ]s. %[message]s']
parent_fhandler. setFormatter[parent_formatter]
parent_logger. addHandler[parent_fhandler]
# Thông báo nhật ký nghiêm trọng INFO hoặc cao hơn sẽ được xử lý
parent_logger. gỡ lỗi['Thông báo gỡ lỗi']
parent_logger. thông tin['Thông tin thông báo']
parent_logger. cảnh báo['Thông báo cảnh báo']
parent_logger. lỗi['Thông báo lỗi']
parent_logger. quan trọng['Thông báo quan trọng']
First, we create a formatter, then set our handler to use that formatter. If we wanted to, we could make several different loggers, handlers, and formatters so that we could mix and match based on our preferences
In this example, the file.log
0 will have
1
2
3
2022-03-23 13. 28. 31,302. WARNING. Warning message
2022-03-23 13. 28. 31,302. ERROR. Error message
2022-03-23 13. 28. 31,303. CRITICAL. Critical message
and the file.log
associated with the handler at root logger will have
1
2
3
4
2022-03-23 13. 28. 31,302. INFO. parent. Info message
2022-03-23 13. 28. 31,302. WARNING. parent. Warning message
2022-03-23 13. 28. 31,302. ERROR. cha mẹ. Error message
2022-03-23 13. 28. 31,303. CRITICAL. parent. Critical message
Below is the visualization of the flow of loggers, handlers, and formatters from
Flow chart of loggers and handlers in the logging module
An Example of the Use of Logging
Let’s consider the Nadam algorithm as an example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# tối ưu hóa độ dốc gốc với nadam cho chức năng kiểm tra hai chiều
từ toán học nhập sqrt
từ numpy nhập asarray
từ numpy. ngẫu nhiên nhập ranh giới
từ numpy. ngẫu nhiên nhập hạt
# hàm mục tiêu
def mục tiêu[x, y]:
return x**2. 0 + y**2. 0
# đạo hàm của hàm mục tiêu
def đạo hàm[x, y]:
trả về xáo trộn[[x * 2.0, y * 2. 0]]
# thuật toán giảm độ dốc với nadam
def nadam[mục tiêu, derivative, bounds, n_iter, alpha, mu, nu, eps=1e-8]:
# tạo điểm ban đầu
x = giới hạn[. , 0] + rand . [len[bounds]] * [bounds[:, 1] - giới hạn . [:, 0]]
điểm = mục tiêu[x[0], x[1]]
# khởi tạo đường trung bình động giảm dần
m = [0. 0 cho _ trong phạm vi . bounds.hình dạng[0]]]
n = [0. 0 cho _ trong phạm vi . bounds.hình dạng[0]]]
# chạy giảm độ dốc
cho t trong phạm vi[n_iter]:
# tính độ dốc g[t]
g = đạo hàm[x[0], x[1]]
# xây dựng giải pháp từng biến một
cho i trong phạm vi[bounds.hình dạng[0]] . :
# m[t] = mu * m[t-1] + [1 - mu] * g[t]
m[i] = mu * m[i] + [1.0 - mu] * g[i]
# n[t] = nu * n[t-1] + [1 - nu] * g[t]^2
n[i] = nu * n[i] + [1.0 - nu] * g[i]**2
# mhat = [mu * m[t] / [1 - mu]] + [[1 - mu] * g[t] / [1 - mu]]
mhat = [mu * m[i] / [1.0 - mu]] . 0 [[1 - mu] * g[i] / [1.0 - mu]]
# nhat = nu * n[t] / [1 - nu]
nhat = nu * n[i] / [1.0 - nu]
# x[t] = x[t-1] - alpha / [sqrt[nhat] + eps] * mhat
x[i] = x[i] - alpha / [sqrt[nhat] + eps] * mhat
# đánh giá điểm ứng viên
điểm = mục tiêu[x[0], x[1]]
# báo cáo tiến độ
in['>%d f[%s] = %. 5f' % [t, x, score]]
return [x, điểm]
# khởi tạo trình tạo số giả ngẫu nhiên
hạt[1]
# xác định phạm vi cho đầu vào
giới hạn = xáo trộn[[[-1.0, 1. 0], [- . 0, 1. 0]]]
# xác định tổng số lần lặp
n_iter = 50
# kích thước bước
alpha = 0. 02
# yếu tố cho độ dốc trung bình
mu = 0. 8
# hệ số cho độ dốc bình phương trung bình
nu = 0. 999
# thực hiện tìm kiếm giảm độ dốc với nadam
tốt nhất, điểm = nadam[objective, derivative, bounds, n_iter, alpha, mu, nu]
in['Xong. ']
in['f[%s] = %f' % [best, score]]
Trường hợp sử dụng đơn giản nhất là sử dụng ghi nhật ký để thay thế hàm print[]
. Chúng ta có thể thực hiện thay đổi sau. Trước tiên, hãy tạo một trình ghi nhật ký có tên file.log
7 trước khi chúng tôi chạy bất kỳ mã nào và chỉ định một logging,
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.. .
import logging
.. .
# Thêm. Tạo logger và chỉ định trình xử lý
ghi nhật ký = ghi nhật ký. getLogger["nadam"]
trình xử lý = ghi nhật ký. StreamHandler[]
trình xử lý. setFormatter[ghi nhật ký. Trình định dạng["%[asctime]s. %[tên cấp độ]s. %[name]s. %[tin nhắn]s"]]
nhật ký. addHandler[trình xử lý]
nhật ký. setLevel[ghi nhật ký. GỠ LỖI]
# khởi tạo trình tạo số giả ngẫu nhiên
hạt[1]
. . . # rest of the code
Chúng tôi phải chỉ định một trình xử lý vì chúng tôi chưa bao giờ định cấu hình bộ ghi gốc và đây sẽ là trình xử lý duy nhất từng được tạo. Then, in the function file.log
9, we re-create a logger logging.basicConfig[]
0 but since it has already been set up, the level and handlers persisted. At the end of each outer for-loop in file.log
9, we replaced the print[]
function with logging.basicConfig[]
3 so the message will be handled by the logging system
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
.. .
# thuật toán giảm độ dốc với nadam
def nadam[mục tiêu, derivative, bounds, n_iter, alpha, mu, nu, eps=1e-8]:
# Create a logger
logger = logging. getLogger["nadam"]
# generate an initial point
x = bounds[. , 0] + rand[len[bounds]] * [bounds[. , 1] - bounds[. , 0]]
score = objective[x[0], x[1]]
# initialize decaying moving averages
m = [0. 0 for _ in range[bounds. shape[0]]]
n = [0. 0 for _ in range[bounds. shape[0]]]
# run the gradient descent
for t in range[n_iter].
# calculate gradient g[t]
g = derivative[x[0], x[1]]
# build a solution one variable at a time
for i in range[bounds. shape[0]].
# m[t] = mu * m[t-1] + [1 - mu] * g[t]
m[i] = mu * m[i] + [1. 0 - mu] * g[i]
# n[t] = nu * n[t-1] + [1 - nu] * g[t]^2
n[i] = nu * n[i] + [1. 0 - nu] * g[i]**2
# mhat = [mu * m[t] / [1 - mu]] + [[1 - mu] * g[t] / [1 - mu]]
mhat = [mu * m[i] / [1. 0 - mu]] + [[1 - mu] * g[i] / [1. 0 - mu]]
# nhat = nu * n[t] / [1 - nu]
nhat = nu * n[i] / [1. 0 - nu]
# x[t] = x[t-1] - alpha / [sqrt[nhat] + eps] * mhat
x[i] = x[i] - alpha / [sqrt[nhat] + eps] * mhat
# evaluate candidate point
score = objective[x[0], x[1]]
# report progress using logger
logger. info['>%d f[%s] = %. 5f' % [t, x, score]]
return [x, score]
.. .
If we are interested in the deeper mechanics of the Nadam algorithm, we may add more logs. The following is the complete code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# tối ưu hóa độ dốc gốc với nadam cho chức năng kiểm tra hai chiều
import logging
từ toán học nhập sqrt
từ numpy nhập asarray
từ numpy. ngẫu nhiên nhập ranh giới
từ numpy. ngẫu nhiên nhập hạt
# hàm mục tiêu
def mục tiêu[x, y]:
return x**2. 0 + y**2. 0
# đạo hàm của hàm mục tiêu
def đạo hàm[x, y]:
return asarray[[x * 2. 0, y * 2. 0]]
# thuật toán giảm độ dốc với nadam
def nadam[mục tiêu, derivative, bounds, n_iter, alpha, mu, nu, eps=1e-8]:
logger = logging. getLogger["nadam"]
# generate an initial point
x = bounds[. , 0] + rand[len[bounds]] * [bounds[. , 1] - bounds[. , 0]]
score = objective[x[0], x[1]]
# initialize decaying moving averages
m = [0. 0 for _ in range[bounds. shape[0]]]
n = [0. 0 for _ in range[bounds. shape[0]]]
# run the gradient descent
for t in range[n_iter].
iterlogger = logging. getLogger["nadam. iter"]
# calculate gradient g[t]
g = derivative[x[0], x[1]]
# build a solution one variable at a time
for i in range[bounds. shape[0]].
# m[t] = mu * m[t-1] + [1 - mu] * g[t]
m[i] = mu * m[i] + [1. 0 - mu] * g[i]
# n[t] = nu * n[t-1] + [1 - nu] * g[t]^2
n[i] = nu * n[i] + [1. 0 - nu] * g[i]**2
# mhat = [mu * m[t] / [1 - mu]] + [[1 - mu] * g[t] / [1 - mu]]
mhat = [mu * m[i] / [1. 0 - mu]] + [[1 - mu] * g[i] / [1. 0 - mu]]
# nhat = nu * n[t] / [1 - nu]
nhat = nu * n[i] / [1. 0 - nu]
# x[t] = x[t-1] - alpha / [sqrt[nhat] + eps] * mhat
x[i] = x[i] - alpha / [sqrt[nhat] + eps] * mhat
iterlogger. info["Iteration %d variable %d. mhat=%f nhat=%f", t, i, mhat, nhat]
# evaluate candidate point
score = objective[x[0], x[1]]
# report progress
logger. info['>%d f[%s] = %. 5f' % [t, x, score]]
return [x, score]
# Create logger and assign handler
ghi nhật ký = ghi nhật ký. getLogger["nadam"]
trình xử lý = ghi nhật ký. StreamHandler[]
trình xử lý. setFormatter[ghi nhật ký. Trình định dạng["%[asctime]s. %[tên cấp độ]s. %[name]s. %[tin nhắn]s"]]
nhật ký. addHandler[trình xử lý]
nhật ký. setLevel[ghi nhật ký. GỠ LỖI]
logger = logging. getLogger["nadam. iter"]
nhật ký. setLevel[logging. INFO]
# khởi tạo trình tạo số giả ngẫu nhiên
hạt[1]
# xác định phạm vi cho đầu vào
giới hạn = xáo trộn[[[-1.0, 1. 0], [- . 0, 1. 0]]]
# xác định tổng số lần lặp
n_iter = 50
# kích thước bước
alpha = 0. 02
# yếu tố cho độ dốc trung bình
mu = 0. 8
# hệ số cho độ dốc bình phương trung bình
nu = 0. 999
# thực hiện tìm kiếm giảm độ dốc với nadam
tốt nhất, điểm = nadam[objective, derivative, bounds, n_iter, alpha, mu, nu]
in['Xong. ']
in['f[%s] = %f' % [best, score]]
We prepared two level of loggers, file.log
7 and logging.basicConfig[]
5, and set them in different levels. In the inner loop of file.log
9, we use the child logger to print some internal variables. When you run this script, it will print the following
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2022-03-29 12. 24. 59,421. INFO. nadam. iter. Iteration 0 variable 0. mhat=-0. 597442 nhat=0. 110055
2022-03-29 12. 24. 59,421. INFO. nadam. iter. Iteration 0 variable 1. mhat=1. 586336 nhat=0. 775909
2022-03-29 12. 24. 59,421. INFO. nadam. >0 f[[-0. 12993798 0. 40463097]] = 0. 18061
2022-03-29 12. 24. 59,421. INFO. nadam. iter. Lặp lại 1 biến 0. mhat=-0. 680200 nhat=0. 177413
2022-03-29 12. 24. 59,421. THÔNG TIN. nadam. lặp đi lặp lại. Lặp 1 biến 1. mhat=2. 020702 nhat=1. 429384
2022-03-29 12. 24. 59,421. THÔNG TIN. nadam. >1 f[[-0. 09764012 0. 37082777]] = 0. 14705
2022-03-29 12. 24. 59,421. THÔNG TIN. nadam. lặp đi lặp lại. Lặp lại 2 biến 0. mhat=-0. 687764 nhat=0. 215332
2022-03-29 12. 24. 59,421. THÔNG TIN. nadam. lặp đi lặp lại. Lặp lại 2 biến 1. mhat=2. 304132 nhat=1. 977457
2022-03-29 12. 24. 59,421. THÔNG TIN. nadam. >2 f[[-0. 06799761 0. 33805721]] = 0. 11891
...
2022-03-29 12. 24. 59,449. THÔNG TIN. nadam. lặp đi lặp lại. Lặp lại 49 biến 0. mhat=-0. 000482 nhat=0. 246709
2022-03-29 12. 24. 59,449. THÔNG TIN. nadam. lặp đi lặp lại. Lặp lại 49 biến 1. mhat=-0. 018244 nhat=3. 966938
2022-03-29 12. 24. 59,449. THÔNG TIN. nadam. >49 f[[-5. 54299505e-05 -1. 00116899e-03]] = 0. 00000
Xong
f[[-5. 54299505e-05 -1. 00116899e-03]] = 0. 000001
Đặt các trình ghi nhật ký khác nhau không chỉ cho phép chúng tôi đặt cấp độ hoặc trình xử lý khác nhau mà còn cho phép chúng tôi phân biệt thông báo nhật ký đến từ đâu bằng cách xem tên của trình ghi nhật ký từ thông báo được in
Trên thực tế, một mẹo hữu ích là tạo trình trang trí ghi nhật ký và áp dụng trình trang trí cho một số chức năng. Chúng ta có thể theo dõi mỗi khi chức năng đó được gọi. Ví dụ: chúng tôi đã tạo một trình trang trí bên dưới và áp dụng nó cho các chức năng logging.basicConfig[]
7 và logging.basicConfig[]
8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
.. .
# Trình trang trí Python để ghi nhật ký lệnh gọi hàm và giá trị trả về
def loggingdecorator[name]:
ghi nhật ký = ghi nhật ký. getLogger[tên]
def _decor[fn]:
tên_hàm = fn. __name__
def _fn[*args, **kwargs]:
ret = fn[*args, **kwargs]
argstr = [str[x] for x in args]
argstr += [key+"="+str[val] for key,val in kwargs.mặt hàng[]]
máy ghi. gỡ lỗi["%s[%s] -> %s" . function_name, ", ".tham gia[argstr], ret]
return ret
return _fn
return _decor
# hàm mục tiêu
@loggingdecorator["nadam. chức năng"]
def mục tiêu[x, y]:
return x**2. 0 + y**2. 0
# đạo hàm của hàm mục tiêu
@loggingdecorator["nadam. chức năng"]
def đạo hàm[x, y]:
return asarray[[x * 2. 0, y * 2. 0]]
Sau đó, chúng ta sẽ thấy những điều sau đây trong nhật ký
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2022-03-29 13. 14. 07,542. GỠ LỖI. nadam. hàm số. mục tiêu[-0. 165955990594852, 0. 4406489868843162] -> 0. 22171292045649288
2022-03-29 13. 14. 07,542. GỠ LỖI. nadam. hàm số. đạo hàm[-0. 165955990594852, 0. 4406489868843162] -> [-0. 33191198 0. 88129797]
2022-03-29 13. 14. 07,542. THÔNG TIN. nadam. lặp đi lặp lại. Lặp lại 0 biến 0. mhat=-0. 597442 nhat=0. 110055
2022-03-29 13. 14. 07,542. THÔNG TIN. nadam. lặp đi lặp lại. Lặp lại 0 biến 1. mhat=1. 586336 nhat=0. 775909
2022-03-29 13. 14. 07,542. GỠ LỖI. nadam. hàm số. mục tiêu[-0. 12993797816930272, 0. 4046309737819536] -> 0. 18061010311445824
2022-03-29 13. 14. 07,543. THÔNG TIN. nadam. >0 f[[-0. 12993798 0. 40463097]] = 0. 18061
2022-03-29 13. 14. 07,543. GỠ LỖI. nadam. hàm số. đạo hàm[-0. 12993797816930272, 0. 4046309737819536] -> [-0. 25987596 0. 80926195]
2022-03-29 13. 14. 07,543. THÔNG TIN. nadam. lặp đi lặp lại. Lặp lại 1 biến 0. mhat=-0. 680200 nhat=0. 177413
2022-03-29 13. 14. 07,543. THÔNG TIN. nadam. lặp đi lặp lại. Lặp 1 biến 1. mhat=2. 020702 nhat=1. 429384
2022-03-29 13. 14. 07,543. GỠ LỖI. nadam. hàm số. mục tiêu[-0. 09764011794760165, 0. 3708277653552375] -> 0. 14704682419118062
2022-03-29 13. 14. 07,543. THÔNG TIN. nadam. >1 f[[-0. 09764012 0. 37082777]] = 0. 14705
2022-03-29 13. 14. 07,543. GỠ LỖI. nadam. hàm số. đạo hàm[-0. 09764011794760165, 0. 3708277653552375] -> [-0. 19528024 0. 74165553]
2022-03-29 13. 14. 07,543. THÔNG TIN. nadam. lặp đi lặp lại. Lặp lại 2 biến 0. mhat=-0. 687764 nhat=0. 215332
...
nơi chúng ta có thể thấy các tham số và giá trị trả về của mỗi cuộc gọi đến hai hàm đó trong thông báo được ghi bởi bộ ghi nhật ký logging.basicConfig[]
9
Bạn muốn bắt đầu với Python cho Machine Learning?
Tham gia khóa học xử lý sự cố email miễn phí trong 7 ngày của tôi ngay bây giờ [có mã mẫu]
Nhấp để đăng ký và cũng nhận được phiên bản PDF Ebook miễn phí của khóa học
Tải xuống khóa học nhỏ MIỄN PHÍ của bạn
Khi chúng tôi nhận được ngày càng nhiều thông báo tường trình, màn hình thiết bị đầu cuối sẽ trở nên rất bận rộn. Một cách để giúp theo dõi các vấn đề dễ dàng hơn là đánh dấu nhật ký bằng màu với mô-đun %[asctime]s
0. Bạn cần cài đặt mô-đun trước
1
pip cài đặt colorama
Here’s an example of how you can use the %[asctime]s
0 module with the %[asctime]s
2 module to change your log colors and text brightness
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import logging
nhập colorama
từ colorama nhập Fore, Back, Style
# Khởi tạo thiết bị đầu cuối cho màu sắc
colorama. init[tự động đặt lại = True]
# Thiết lập nhật ký như bình thường
ghi nhật ký = ghi nhật ký. getLogger["color"]
nhật ký. setLevel[ghi nhật ký. GỠ LỖI]
trình xử lý = ghi nhật ký. StreamHandler[]
trình định dạng = ghi nhật ký. Trình định dạng['%[asctime]s. %[tên cấp độ]s. %[name]s. %[message]s']
người xử lý. setFormatter[bộ định dạng]
nhật ký. addHandler[handler]
# Phát ra thông điệp tường trình với màu sắc
nhật ký. gỡ lỗi['Thông báo gỡ lỗi']
nhật ký. vào[Đối với. XANH LỤC + 'Thông báo thông tin']
nhật ký. cảnh báo[Trước. BLUE + 'Thông báo cảnh báo']
nhật ký. lỗi[Trước. VÀNG + Kiểu dáng. SÁNG + 'Thông báo lỗi']
nhật ký. quan trọng[Trước. ĐỎ + Quay lại. VÀNG + Kiểu dáng. SÁNG + 'Thông báo quan trọng']
Từ thiết bị đầu cuối, bạn sẽ thấy như sau
trong đó %[asctime]s
3, %[asctime]s
4 và %[asctime]s
5 từ mô-đun %[asctime]s
0 điều khiển kiểu nền trước, nền sau và độ sáng của văn bản được in. Điều này đang tận dụng các ký tự thoát ANSI và chỉ hoạt động trên các thiết bị đầu cuối được ANSI hỗ trợ. Do đó, điều này không phù hợp để đăng nhập vào tệp văn bản
Trên thực tế, chúng ta có thể dẫn xuất lớp print[]
4 với
1
2
3
4
5
6
7
8
9
.. .
colors = {"DEBUG". Dành cho. XANH DƯƠNG, "THÔNG TIN". Cho. CYAN,
"CẢNH BÁO". Cho. VÀNG, "ERROR". Trước. ĐỎ, "CẤM". Trước. MAGENTA}
lớp ColoredFormatter[ghi nhật ký. Trình định dạng].
def định dạng[chính, record]:
tin nhắn = ghi nhật ký. Trình định dạng. định dạng[bản thân, bản ghi]
nếu ghi. tên cấp độ trong màu sắc.
tin nhắn = màu sắc[record.tên cấp độ] + tin nhắn + . Fore.ĐẶT LẠI
trả về tin nhắn
và sử dụng cái này thay cho %[asctime]s
8. Sau đây là cách chúng ta có thể sửa đổi thêm ví dụ Nadam để thêm màu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# tối ưu hóa độ dốc gốc với nadam cho chức năng kiểm tra hai chiều
import logging
nhập colorama
từ colorama nhập Fore
từ toán học nhập sqrt
từ numpy nhập asarray
từ numpy. ngẫu nhiên nhập ranh giới
từ numpy. ngẫu nhiên nhập hạt
def loggingdecorator[name]:
ghi nhật ký = ghi nhật ký. getLogger[tên]
def _decor[fn]:
tên_hàm = fn. __name__
def _fn[*args, **kwargs]:
ret = fn[*args, **kwargs]
argstr = [str[x] for x in args]
argstr += [key+"="+str[val] for key,val in kwargs.mặt hàng[]]
máy ghi. gỡ lỗi["%s[%s] -> %s" . function_name, ", ".tham gia[argstr], ret]
return ret
return _fn
return _decor
# hàm mục tiêu
@loggingdecorator["nadam. chức năng"]
def mục tiêu[x, y]:
return x**2. 0 + y**2. 0
# đạo hàm của hàm mục tiêu
@loggingdecorator["nadam. chức năng"]
def đạo hàm[x, y]:
return asarray[[x * 2. 0, y * 2. 0]]
# thuật toán giảm độ dốc với nadam
def nadam[mục tiêu, derivative, bounds, n_iter, alpha, mu, nu, eps=1e-8]:
logger = logging. getLogger["nadam"]
# generate an initial point
x = bounds[. , 0] + rand[len[bounds]] * [bounds[. , 1] - bounds[. , 0]]
score = objective[x[0], x[1]]
# initialize decaying moving averages
m = [0. 0 for _ in range[bounds. shape[0]]]
n = [0. 0 for _ in range[bounds. shape[0]]]
# run the gradient descent
for t in range[n_iter].
iterlogger = logging. getLogger["nadam. iter"]
# calculate gradient g[t]
g = derivative[x[0], x[1]]
# build a solution one variable at a time
for i in range[bounds. shape[0]].
# m[t] = mu * m[t-1] + [1 - mu] * g[t]
m[i] = mu * m[i] + [1. 0 - mu] * g[i]
# n[t] = nu * n[t-1] + [1 - nu] * g[t]^2
n[i] = nu * n[i] + [1. 0 - nu] * g[i]**2
# mhat = [mu * m[t] / [1 - mu]] + [[1 - mu] * g[t] / [1 - mu]]
mhat = [mu * m[i] / [1. 0 - mu]] + [[1 - mu] * g[i] / [1. 0 - mu]]
# nhat = nu * n[t] / [1 - nu]
nhat = nu * n[i] / [1. 0 - nu]
# x[t] = x[t-1] - alpha / [sqrt[nhat] + eps] * mhat
x[i] = x[i] - alpha / [sqrt[nhat] + eps] * mhat
iterlogger. info["Iteration %d variable %d. mhat=%f nhat=%f", t, i, mhat, nhat]
# evaluate candidate point
score = objective[x[0], x[1]]
# report progress
máy ghi. cảnh báo['>%d f[%s] = %. 5f' % [t, x, score]]
return [x, score]
# Chuẩn bị định dạng màu
colorama. init[tự động đặt lại = True]
colors = {"DEBUG". Dành cho. XANH DƯƠNG, "THÔNG TIN". Cho. CYAN,
"CẢNH BÁO". Cho. VÀNG, "ERROR". Trước. ĐỎ, "CẤM". Trước. MAGENTA}
lớp ColoredFormatter[ghi nhật ký. Trình định dạng].
def định dạng[chính, record]:
tin nhắn = ghi nhật ký. Trình định dạng. định dạng[bản thân, bản ghi]
nếu ghi. tên cấp độ trong màu sắc.
tin nhắn = màu sắc[record.tên cấp độ] + tin nhắn + . Fore.ĐẶT LẠI
trả về tin nhắn
# Create logger and assign handler
ghi nhật ký = ghi nhật ký. getLogger["nadam"]
trình xử lý = ghi nhật ký. StreamHandler[]
trình xử lý. setFormatter[ColoredFormatter[" . %[levelname]s. %[name]s. %[tin nhắn]s"]]
nhật ký. addHandler[trình xử lý]
nhật ký. setLevel[ghi nhật ký. GỠ LỖI]
logger = logging. getLogger["nadam. iter"]
nhật ký. setLevel[ghi nhật ký. GỠ LỖI]
# khởi tạo trình tạo số giả ngẫu nhiên
hạt[1]
# xác định phạm vi cho đầu vào
giới hạn = xáo trộn[[[-1.0, 1. 0], [- . 0, 1. 0]]]
# xác định tổng số lần lặp
n_iter = 50
# kích thước bước
alpha = 0. 02
# yếu tố cho độ dốc trung bình
mu = 0. 8
# hệ số cho độ dốc bình phương trung bình
nu = 0. 999
# thực hiện tìm kiếm giảm độ dốc với nadam
tốt nhất, điểm = nadam[objective, derivative, bounds, n_iter, alpha, mu, nu]
in['Xong. ']
in['f[%s] = %f' % [best, score]]
Nếu chúng ta chạy nó trên một thiết bị đầu cuối hỗ trợ, chúng ta sẽ thấy đầu ra sau
Note that the colorful output can help us spot any abnormal behavior easier. Ghi nhật ký giúp gỡ lỗi và cũng cho phép chúng tôi dễ dàng kiểm soát mức độ chi tiết mà chúng tôi muốn xem bằng cách chỉ thay đổi một vài dòng mã