Python chạy chương trình bên ngoài và nhận đầu ra

Python, là ngôn ngữ lập trình có mục đích chung, cho phép bạn chạy các chương trình bên ngoài từ tập lệnh của mình và ghi lại đầu ra của chúng. Điều này hữu ích cho nhiều tác vụ học máy trong đó một người muốn sử dụng ứng dụng dòng lệnh trong một đường dẫn do Python điều khiển. Ví dụ: chúng tôi điều tra cách kích thước bảng băm của Vowpal Wabbit ảnh hưởng đến điểm xác thực

Động lực

Một số người dường như gắn bó với các công cụ Python gốc như scikit-learning, bởi vì chúng mang lại sự thoải mái cho giải pháp đầu cuối. Mặt khác, các tệp nhị phân dòng lệnh có thể nhanh hơn và cung cấp chức năng không có sẵn trong Python

May mắn thay, ngôn ngữ có các tiện ích để tích hợp các công cụ như vậy. Chúng ta sẽ xem xét hai chức năng. hệ điều hành. hệ thống () và quy trình con. check_output(). Sự khác biệt chính giữa chúng là cái đầu tiên không nắm bắt được đầu ra của lệnh, trong khi cái sau thì không. Tuy nhiên, với

cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
1, người ta có thể chuyển hướng đầu ra sang một tệp và sau đó đọc từ tệp đó, như chúng tôi minh họa bên dưới

Nhược điểm của cả hai phương pháp là bạn không thể thấy đầu ra từ chương trình bên ngoài khi nó đang chạy. Trên Unix có một cách để khắc phục điều này.

cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
2. Lệnh in đầu vào của nó và cũng ghi nó vào một tệp, vì vậy bạn có thể vừa xem những gì đang diễn ra vừa ghi nhật ký

Ví dụ: chúng tôi sẽ chạy Vowpal Wabbit từ Python để kiểm tra xem tùy chọn

cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
3 ảnh hưởng đến điểm số như thế nào. Tùy chọn kiểm soát kích thước của bảng băm và thường cao hơn sẽ tốt hơn, vì kích thước bảng nhỏ dẫn đến xung đột băm

cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1 | tee {}'.format( 
    path_to_cache, b, tmp_log_file )
os.system( cmd )
output = open( tmp_log_file, 'r' ).read()

Lệnh này làm cho VW đọc tệp bộ đệm đã cho làm đầu vào. Với

cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
4, chúng tôi chuyển đầu ra thành
cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
2, lưu nó vào
cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
1. Sau đó, chúng tôi đọc nó. Nhưng
cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
2 có nghĩa là gì?

luồng vào/ra

Một tính năng hữu ích khác của Unix, cũng có sẵn một phần trên Windows - theo kiểu nửa vời điển hình - là khái niệm luồng I/O. đầu vào tiêu chuẩn, đầu ra tiêu chuẩn và sai số tiêu chuẩn. Đặc biệt, sự phân chia giữa các luồng đầu ra và lỗi là điều chúng tôi quan tâm. Thông thường, một chương trình dòng lệnh in đầu ra bình thường của nó thành thiết bị xuất chuẩn và thông báo lỗi cho thiết bị xuất chuẩn - theo cách này chúng không bị lẫn lộn

Đôi khi người ta muốn kết hợp thiết bị xuất chuẩn và thiết bị xuất chuẩn thành một luồng duy nhất. Các luồng tiêu chuẩn có các số lần lượt là 0, 1 và 2 cho đầu vào, đầu ra và lỗi. Do đó, để chuyển hướng thiết bị xuất chuẩn sang thiết bị xuất chuẩn (như chúng tôi đã làm ở trên) trong hệ vỏ Unix, bạn sẽ viết

cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
3

Lưu ý dấu và ở phía trước của

cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
4 - không có nó, trình bao sẽ coi
cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
4 là tên tệp

Nhân tiện, nếu bạn muốn chuyển hướng cả thiết bị xuất chuẩn và thiết bị xuất chuẩn sang một tệp, thì có một phím tắt cho điều đó (chỉ dành cho Unix)

cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
6

Bây giờ là ví dụ về

cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
7 - nó có cấu trúc hơi khác một chút, nhưng thực hiện tương tự. Lưu ý rằng chúng tôi không cần sử dụng tệp tạm thời

cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )

Trên Unix, có thể tham chiếu đến các luồng theo tên hệ thống tệp của chúng.

cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
8,
cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
9 và
cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
10. Trong ví dụ trên, chúng tôi sử dụng một mẹo nhỏ. vì chức năng bắt đầu ra tiêu chuẩn, nhưng không phải lỗi tiêu chuẩn, chúng tôi
cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
2 đầu ra thành
cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
10 để nó được in trên màn hình đồng thời chuyển sang đầu ra tiêu chuẩn và được ghi lại

Đối số

cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
13 khiến hàm thực thi một lệnh đã cho bằng trình bao. Trong thực tế, điều đó có nghĩa là bạn có thể truyền các đối số dưới dạng chuỗi, thay vì danh sách, như
cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
7 thường mong đợi

Xử lý đầu ra

Khi chúng tôi có đầu ra, chúng tôi có thể trích xuất thông tin chúng tôi muốn từ nó. Điều này có thể được thực hiện với các biểu thức thông thường. Bản thân chúng là một ngôn ngữ, nhưng ngay cả một chút khớp mẫu cũng giúp bạn đi được một chặng đường dài

Vấn đề là, trong đầu ra của VW có một dòng thông tin về tổn thất trung bình, giống như thế này

cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
1

Chúng tôi muốn khớp mẫu đó và trích xuất số, và chức năng này thực hiện thủ thuật

cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
6

Nói tóm lại, các dấu ngoặc đơn trong mẫu xác định một nhóm (phụ) mà chúng tôi muốn nắm bắt. Dấu chấm là viết tắt của bất kỳ ký tự nào và dấu hoa thị có nghĩa là “không hoặc nhiều lần”, vì vậy,

cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
15 có nghĩa là “bất kỳ ký tự nào không hoặc nhiều lần, theo sau là một dòng mới”. Cuối cùng, dấu chấm hỏi làm cho
cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
16 không tham lam để nó dừng ở dòng mới đầu tiên mà nó nhìn thấy

Khi thực hiện nhiều lần vượt qua, VW sẽ cho biết bằng cách sử dụng bộ giữ lại để tính toán tổn thất

cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
9

Để giải thích cho điều này, chúng tôi chỉ khớp các chữ số, dấu chấm và 'e' (trong trường hợp VW tình cờ in các số theo ký hiệu khoa học)

cmd = 'vw --loss_function logistic --cache_file {} -b {} 2>&1'.format( 
    path_to_cache, b )  
output = subprocess.check_output( '{} | tee /dev/stderr'.format( cmd ), shell = True )
0

Nhóm chúng tôi đang trích xuất là “một hoặc nhiều ký tự từ những ký tự trong ngoặc”. Phần còn lại hoạt động tương tự

Để tất cả chúng cùng nhau

Bây giờ chúng ta đã biết cách gọi VW từ Python và trích xuất các phần thú vị từ đầu ra của nó, hãy vẽ sơ đồ lỗi đối với các bit bảng băm được sử dụng. Chúng tôi đã kiểm tra phạm vi từ 7 đến 29 bit trên tập dữ liệu KDD10B bằng cách sử dụng một lượt và không có tính năng bậc hai

Python chạy chương trình bên ngoài và nhận đầu ra

Mất nhật ký trung bình cho KDD10b, một lượt, không có tính năng bậc hai

Có vẻ như đối với trường hợp này, số bit tối ưu là 25. Giá trị lớn hơn sử dụng nhiều bộ nhớ hơn nhưng không cải thiện điểm số

Đây là một biểu đồ tương tự cho tập dữ liệu Amazon Access Control với các tính năng bậc hai, nhiều lượt và thiết lập giữ lại/dừng sớm. Rõ ràng là có rất ít lợi ích khi tăng số bit vượt quá 20

Python chạy chương trình bên ngoài và nhận đầu ra

Mất nhật ký trung bình đối với bộ dữ liệu Amazon Access Control, sử dụng các tính năng bậc hai và nhiều lần dừng sớm

Mã có sẵn tại GitHub

Cuối cùng, nếu bạn đặc biệt quan tâm đến việc chạy VW từ Python, bạn có thể xem thử nguyện vọng, Wabbit Wappa hoặc Hal Daume's pyvw

CẬP NHẬT. Kiểm tra thư viện plumbum, cố gắng làm cho việc chạy các lệnh shell từ Python trở nên dễ dàng