python - 是否可以在内存中保持空间以减少加载时间?

标签 python nlp spacy

<分区>

我想使用 spacy 作为 NLP 的在线服务。 每次用户发出请求时,我都会调用脚本“my_script.py”

开始于:

from spacy.en import English
nlp = English()

我遇到的问题是这两行需要超过 10 秒,是否可以将 English() 保留在 ram 中或其他一些选项以将此加载时间减少到不到一秒?

最佳答案

您说您希望在收到请求时启动独立脚本 (my_script.py)。这将使用来自 spacy.en 的功能,而不会产生开销加载 spacy.en。使用这种方法,操作系统将始终在您启动脚本时创建一个新进程。所以只有一种方法可以避免每次都加载 spacy.en:有一个单独的进程已经在运行,加载了 spacy.en,并让你的脚本与那个过程。下面的代码显示了一种方法来做到这一点。但是,正如其他人所说,您可能会因更改服务器架构而受益,因此 spacy.en 会加载到您的网络服务器中(例如,使用基于 Python 的网络服务器)。

最常见的进程间通信形式是通过 TCP/IP 套接字。下面的代码实现了一个小型服务器,它保持加载 spacy.en 并处理来自客户端的请求。它还有一个客户端,可以将请求传输到该服务器并接收返回的结果。由您决定将什么放入这些传输中。

还有第三个脚本。由于客户端和服务器都需要发送和接收函数,因此这些函数位于名为 comm.py 的共享脚本中。 (请注意,客户端和服务器各自加载一个单独的 comm.py 副本;它们不通过加载到共享内存中的单个模块进行通信。)

我假设这两个脚本都在同一台机器上运行。如果没有,您需要将 comm.py 的副本放在两台机器上,并将 comm.server_host 更改为服务器的机器名称或 IP 地址。

nlp_server.py 作为后台进程运行(或者只是在不同的终端窗口中进行测试)。这会等待请求、处理它们并将结果发回:

import comm
import socket
from spacy.en import English
nlp = English()

def process_connection(sock):
    print "processing transmission from client..."
    # receive data from the client
    data = comm.receive_data(sock)
    # do something with the data
    result = {"data received": data}
    # send the result back to the client
    comm.send_data(result, sock)
    # close the socket with this particular client
    sock.close()
    print "finished processing transmission from client..."

server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# open socket even if it was used recently (e.g., server restart)
server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_sock.bind((comm.server_host, comm.server_port))
# queue up to 5 connections
server_sock.listen(5)
print "listening on port {}...".format(comm.server_port)
try:
    while True:
        # accept connections from clients
        (client_sock, address) = server_sock.accept()
        # process this connection 
        # (this could be launched in a separate thread or process)
        process_connection(client_sock)
except KeyboardInterrupt:
    print "Server process terminated."
finally:
    server_sock.close()

加载 my_script.py 作为快速运行的脚本以从 nlp 服务器请求结果(例如,python my_script.py here are some arguments):

import socket, sys
import comm

# data can be whatever you want (even just sys.argv)
data = sys.argv

print "sending to server:"
print data

# send data to the server and receive a result
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# disable Nagle algorithm (probably only needed over a network) 
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True)
sock.connect((comm.server_host, comm.server_port))
comm.send_data(data, sock)
result = comm.receive_data(sock)
sock.close()

# do something with the result...
print "result from server:"
print result

comm.py 包含客户端和服务器都使用的代码:

import sys, struct
import cPickle as pickle

# pick a port that is not used by any other process
server_port = 17001
server_host = '127.0.0.1' # localhost
message_size = 8192
# code to use with struct.pack to convert transmission size (int) 
# to a byte string
header_pack_code = '>I'
# number of bytes used to represent size of each transmission
# (corresponds to header_pack_code)
header_size = 4  

def send_data(data_object, sock):
    # serialize the data so it can be sent through a socket
    data_string = pickle.dumps(data_object, -1)
    data_len = len(data_string)
    # send a header showing the length, packed into 4 bytes
    sock.sendall(struct.pack(header_pack_code, data_len))
    # send the data
    sock.sendall(data_string)

def receive_data(sock):
    """ Receive a transmission via a socket, and convert it back into a binary object. """
    # This runs as a loop because the message may be broken into arbitrary-size chunks.
    # This assumes each transmission starts with a 4-byte binary header showing the size of the transmission.
    # See https://docs.python.org/3/howto/sockets.html
    # and http://code.activestate.com/recipes/408859-socketrecv-three-ways-to-turn-it-into-recvall/

    header_data = ''
    header_done = False
    # set dummy values to start the loop
    received_len = 0
    transmission_size = sys.maxint

    while received_len < transmission_size:
        sock_data = sock.recv(message_size)
        if not header_done:
            # still receiving header info
            header_data += sock_data
            if len(header_data) >= header_size:
                header_done = True
                # split the already-received data between header and body
                messages = [header_data[header_size:]]
                received_len = len(messages[0])
                header_data = header_data[:header_size]
                # find actual size of transmission
                transmission_size = struct.unpack(header_pack_code, header_data)[0]
        else:
            # already receiving data
            received_len += len(sock_data)
            messages.append(sock_data)

    # combine messages into a single string
    data_string = ''.join(messages)
    # convert to an object
    data_object = pickle.loads(data_string)
    return data_object

注意:您应确保从服务器发送的结果仅使用 native 数据结构(字典、列表、字符串等)。如果结果包含 spacy.en 中定义的对象,则客户端在解包结果时会自动导入 spacy.en,以提供该对象的方法。

此设置与 HTTP 协议(protocol)非常相似(服务器等待连接,客户端连接,客户端发送请求,服务器发送响应,双方断开连接)。因此,您最好使用标准的 HTTP 服务器和客户端而不是此自定义代码。那将是一个“RESTful API”,这是当今流行的术语(有充分的理由)。使用标准的 HTTP 包可以省去管理自己的客户端/服务器代码的麻烦,您甚至可以直接从现有的 Web 服务器调用数据处理服务器,而无需启动 my_script.py .但是,您必须将您的请求转换为与 HTTP 兼容的内容,例如 GET 或 POST 请求,或者可能只是一个特殊格式的 URL。

另一种选择是使用标准的进程间通信包,例如 PyZMQ、redis、mpi4py 或者 zmq_object_exchanger。请参阅此问题以获取一些想法:Efficient Python IPC

或者您可以使用 dill 包 ( https://pypi.python.org/pypi/dill ) 在磁盘上保存 spacy.en 对象的副本,然后在开始时恢复它my_script.py。这可能比每次导入/重建它更快,并且比使用进程间通信更简单。

关于python - 是否可以在内存中保持空间以减少加载时间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43554124/

相关文章:

python脚本无法读取csv文件并出错 - StopIteration

python - os.rename 不起作用

Python macOS 构建从终端运行,但在 Finder 启动时崩溃

elasticsearch - 在Elasticsearch中使用NLP标签

python - 如何验证安装的 spaCy 版本?

python - Django 开发版本与稳定版本

c++ - 使用 llvm-gcc 4.x 安装 srilm 1.6.0 时出现问题

spacy - Spacy 中的多词表达识别

python - SpaCy 提取形容词,位于动词之前,不是停用词也不是标点符号

nlp - 在 spaCy 中, 'en' 和 'en_core_web' 模型是否不同?