{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# SPU Basics\n", "\n", ">The following codes are demos only. It's **NOT for production** due to system security concerns, please **DO NOT** use it directly in production.\n", "\n", "SPU devices are responsible for performing MPC computation in SecretFlow.\n", "\n", "This tutorial would help you:\n", "\n", "- be familiar with SPU device and SPU Object\n", "- learn how to transfer a Python Object / PYU Object from/to SPU Object.\n", "- run MPC computation with SPU device.\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create an SPU Device\n", "\n", "### Create SecretFlow Parties\n", "\n", "Parties are basic nodes in SecretFlow nodes. We are going to create four parties - **alice**, **bob**, **carol** and **dave**.\n", "\n", "Based on four parties, we will set up three devices:\n", "\n", "- a PYU device based on *alice*\n", "- a PYU device based on *dave*\n", "- an SPU device based on *alice*, *bob* and *carol*\n", "\n", "\"spu_basics_devices.png\"\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2022-08-30 18:34:37.024687: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /opt/rh/rh-ruby25/root/usr/local/lib64:/opt/rh/rh-ruby25/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib:/opt/rh/devtoolset-11/root/usr/lib64/dyninst:/opt/rh/devtoolset-11/root/usr/lib/dyninst\n" ] } ], "source": [ "import secretflow as sf\n", "\n", "# In case you have a running secretflow runtime already.\n", "sf.shutdown()\n", "\n", "sf.init(['alice', 'bob', 'carol', 'dave'], address='local')\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Create a 3PC ABY3 SPU device" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After that, let's create an SPU device with [ABY3](https://eprint.iacr.org/2018/403.pdf) protocol.\n", "\n", "`sf.utils.testing.cluster_def` is a helper method to create a config by finding unused ports." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'nodes': [{'party': 'alice', 'id': 'local:0', 'address': '127.0.0.1:23669'},\n", " {'party': 'bob', 'id': 'local:1', 'address': '127.0.0.1:54219'},\n", " {'party': 'carol', 'id': 'local:2', 'address': '127.0.0.1:27519'}],\n", " 'runtime_config': {'protocol': 3, 'field': 3}}" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "aby3_config = sf.utils.testing.cluster_def(parties=['alice', 'bob', 'carol'])\n", "\n", "aby3_config\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then let's use *aby3_config* to create an SPU device and check its cluster_def." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'nodes': [{'party': 'alice', 'id': 'local:0', 'address': '127.0.0.1:23669'},\n", " {'party': 'bob', 'id': 'local:1', 'address': '127.0.0.1:54219'},\n", " {'party': 'carol', 'id': 'local:2', 'address': '127.0.0.1:27519'}],\n", " 'runtime_config': {'protocol': 3, 'field': 3}}" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "spu_device = sf.SPU(aby3_config)\n", "\n", "spu_device.cluster_def\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lastly, let's create two PYU devices." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "alice, dave = sf.PYU('alice'), sf.PYU('dave')\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Pass Values to SPU device\n", "\n", "Before talking about computation with SPU device, let's understand how to pass a Python object / PYUObject to SPU device." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Pass a Python Object from Host to SPU\n", "\n", "Let's pass a dict from Host to SPU device." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "WARNING:absl:No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)\n" ] } ], "source": [ "bank_account = [{'id': 12345, 'deposit': 1000.25}, {'id': 12345, 'deposit': 100000.25}]\n", "\n", "bank_account_spu = sf.to(alice, bank_account).to(spu_device)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*bank_account_spu* is a **SPUObject**. A *SPUObject* represents a Python Object which could be consumed by an SPU device." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "secretflow.device.device.spu.SPUObject" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(bank_account_spu)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Each SPUObject has two fields:\n", "\n", "- meta\n", "- shares\n", "\n", "At this moment, since we are creating an SPU object from **Host**. We could check these two fields freely.\n", "\n", "Let's check meta first." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'deposit': SPUValueMeta(shape=(), dtype=dtype('float32'), vtype=1),\n", " 'id': SPUValueMeta(shape=(), dtype=dtype('int32'), vtype=1)},\n", " {'deposit': SPUValueMeta(shape=(), dtype=dtype('float32'), vtype=1),\n", " 'id': SPUValueMeta(shape=(), dtype=dtype('int32'), vtype=1)}]" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bank_account_spu.meta\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I guess you could find meta preserves the structure of origin data and replaces the digits/arrays with **SPUValueMeta**.\n", "\n", "Then let's check *shares* of bank_account_spu. Since we are passing data to a 3PC SPU device. We would have three pieces of shares,\n", "and we are going to check the first piece." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'deposit': data_type: DT_FXP\n", " visibility: VIS_SECRET\n", " storage_type: \"aby3.AShr\"\n", " content: \"\\361\\177j\\273\\313\\270\\3253Y\\034\\370WM&K\\r\\221\\360y\\223\\371m\\272L\\037<\\233W\\2271\\221c\",\n", " 'id': data_type: DT_I32\n", " visibility: VIS_SECRET\n", " storage_type: \"aby3.AShr\"\n", " content: \"\\227\\357\\204\\032\\363\\201\\307\\234 f\\272\\361\\216\\305\\265\\373\\332\\301\\314!\\366\\360\\241x\\245T\\231\\267\\320d]\\202\"},\n", " {'deposit': data_type: DT_FXP\n", " visibility: VIS_SECRET\n", " storage_type: \"aby3.AShr\"\n", " content: \"j\\240s\\364=\\365\\243j\\315:\\214\\036:\\233xYrK\\304\\201\\245G\\350ER\\360\\007\\274\\2365M\\376\",\n", " 'id': data_type: DT_I32\n", " visibility: VIS_SECRET\n", " storage_type: \"aby3.AShr\"\n", " content: \";!\\317\\t\\302\\227\\270n\\324}\\003\\327\\365\\2747\\215\\362BC8U:I\\232B<\\226\\213\\235\\241$x\"}]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "assert len(bank_account_spu.shares_name) == 3\n", "\n", "bank_account_spu.shares_name[0]\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You should find a piece of shares of SPU Object is very similar to meta and origin data. It still preserves the structure of origin data while digits are replaced by a struct consisting of:\n", "\n", "- data_type, indicates whether the value is integer or fixed points.\n", "- visibility, indicates whether the value is a secret or a public content. \n", "- storage_type, indicates attributes of value, e.g. MPC protocol(ABY3 in our case), field size(128 bits in our case), etc\n", "- content, encoded secret (try to guess the origin data if you would like to)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Pass a PYU Object from PYU to SPU\n", "\n", "Then let's try another path.\n", "First, we create a PYU object with a PYU device." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def debit_amount():\n", " return 10\n", "\n", "\n", "debit_amount_pyu = alice(debit_amount)()\n", "debit_amount_pyu\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then let's pass debit_amount_pyu from PYU to SPU. We will get an SPU object as result." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "debit_amount_spu = debit_amount_pyu.to(spu_device)\n", "\n", "debit_amount_spu\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's check meta of debit_amount_spu." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "ObjectRef(4ee449587774c1f0ffffffffffffffffffffffff0100000001000000)" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "debit_amount_spu.meta\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Oh no, it's a Ray ObjectRef located at alice part.\n", "So how about shares of debit_amount_spu?" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\u001b[2m\u001b[36m(SPURuntime pid=922821)\u001b[0m I0830 18:34:44.759606 922821 external/com_github_brpc_brpc/src/brpc/server.cpp:1066] Server[yacl::link::internal::ReceiverServiceImpl] is serving on port=23669.\n", "\u001b[2m\u001b[36m(SPURuntime pid=922821)\u001b[0m I0830 18:34:44.759674 922821 external/com_github_brpc_brpc/src/brpc/server.cpp:1069] Check out http://k69b13338.eu95sqa:23669 in web browser.\n", "\u001b[2m\u001b[36m(SPURuntime pid=922814)\u001b[0m I0830 18:34:44.692403 922814 external/com_github_brpc_brpc/src/brpc/server.cpp:1066] Server[yacl::link::internal::ReceiverServiceImpl] is serving on port=27519.\n", "\u001b[2m\u001b[36m(SPURuntime pid=922814)\u001b[0m I0830 18:34:44.692477 922814 external/com_github_brpc_brpc/src/brpc/server.cpp:1069] Check out http://k69b13338.eu95sqa:27519 in web browser.\n", "\u001b[2m\u001b[36m(SPURuntime pid=922820)\u001b[0m I0830 18:34:44.748833 922820 external/com_github_brpc_brpc/src/brpc/server.cpp:1066] Server[yacl::link::internal::ReceiverServiceImpl] is serving on port=54219.\n", "\u001b[2m\u001b[36m(SPURuntime pid=922820)\u001b[0m I0830 18:34:44.748898 922820 external/com_github_brpc_brpc/src/brpc/server.cpp:1069] Check out http://k69b13338.eu95sqa:54219 in web browser.\n" ] }, { "data": { "text/plain": [ "[ObjectRef(4ee449587774c1f0ffffffffffffffffffffffff0100000002000000),\n", " ObjectRef(4ee449587774c1f0ffffffffffffffffffffffff0100000003000000),\n", " ObjectRef(4ee449587774c1f0ffffffffffffffffffffffff0100000004000000)]" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "debit_amount_spu.shares_name\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So you get a list of ObjectRef! Since it's located at alice part, we couldn't check the value at host." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But if you are really curious, we could use **sf.reveal** to check the origin value. Be careful to use **sf.reveal** in production!" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\u001b[2m\u001b[36m(_run pid=922818)\u001b[0m 2022-08-30 18:34:45.224087: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /opt/rh/rh-ruby25/root/usr/local/lib64:/opt/rh/rh-ruby25/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib:/opt/rh/devtoolset-11/root/usr/lib64/dyninst:/opt/rh/devtoolset-11/root/usr/lib/dyninst\n" ] }, { "data": { "text/plain": [ "array(10, dtype=int32)" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sf.reveal(debit_amount_spu)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is the first part of Data Flow with SPU device, at this moment, you should be aware of the following facts.\n", "\n", "- A Python Object/PYU Object could be transferred to an SPU Object.\n", "- An SPU Object consists of meta and shares.\n", "- You could only check meta and shares when SPU Object is located at host. Otherwise, you have to call *sf.reveal*\n", "- Just converting to SPU Object won't trigger data flow from host / PYU to SPU. e.g. When you transferred a PYU object to an SPU object. All the field of SPU objects including meta and shares are still located at owners(Host / PYU device). The shares would only be sent to parties of SPU device when computation do happen. In short, data flow is lazy." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Computation with SPU Device\n", "\n", "Since we have two SPU objects - *bank_account_spu* and *debit_amount_spu* as inputs.\n", "Let's try to do some computation with SPU device.\n" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\u001b[2m\u001b[36m(_run pid=922818)\u001b[0m 2022-08-30 18:34:46,912,912 WARNING [xla_bridge.py:backends:265] No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def deduce_from_account(bank_account, amount):\n", " new_bank_account = []\n", "\n", " for account in bank_account:\n", " account['deposit'] = account['deposit'] - amount\n", " new_bank_account.append(account)\n", "\n", " return new_bank_account\n", "\n", "\n", "new_bank_account_spu = spu_device(deduce_from_account)(\n", " bank_account_spu, debit_amount_spu\n", ")\n", "\n", "new_bank_account_spu\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*new_bank_account_spu* is also a **SPUObject**. But it's a bit different from *bank_account_spu* and *debit_amount_spu*!\n", "\n", "- *bank_account_spu* is located at host, so you could check value from host directly.\n", "- *debit_amount_spu* is located at alice, so only alice could check value.\n", "- *new_bank_account_spu* is located at spu, each party of spu have a piece of shares. And you couldn't check the value directly without *sf.reveal*.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Well, but what happened behind computation of SPU device?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Step 1: Compile Python(Jax) Code to SPU Executable\n", "\n", "The Python function (*deduce_from_account* in our case) and metas of all inputs (*bank_account_spu* and *debit_amount_spu*) would be sent to one party of SPU device. Then SPU compiler would be used to compile them to *SPU Executable*.\n", "\n", "\"spu_basics_compiler.png\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Step 2: Distribute the SPU Executable and Shares to SPU parties.\n", "\n", "Each party of SPU device would get:\n", "\n", "- one copy of SPU Executable\n", "- one piece of each SPU Object share\n", "\n", "\"spu_basics_distribute.png\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Step 3: Run SPU Executable and Assembly SPU Object\n", "\n", "Then each party of SPU device would execute SPU Executable.\n", "\n", "In the end, each party of SPU device would own a piece of output SPU Objects and a copy of meta.\n", "\n", "Then SecretFlow framework would use them to assembly SPU Objects." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Get Value from SPU Device\n", "\n", "But in the end, we need to get value from spu, we couldn't always keep *SPUObject* as secret!\n", "\n", "Most common way of handling *SPUObject* is pass the secret to one party. This party is not necessarily one of parties consisting of SPU device." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "new_bank_account_pyu = new_bank_account_spu.to(dave)\n", "\n", "new_bank_account_pyu\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We just pass *new_bank_account_spu* to **pyu**, then it becomes a *PYUObject*! And it's owned by dave.\n", "Let's check the value of *new_bank_account_pyu*." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'deposit': array(990.25, dtype=float32), 'id': array(12345, dtype=int32)},\n", " {'deposit': array(99990.25, dtype=float32), 'id': array(12345, dtype=int32)}]" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sf.reveal(new_bank_account_pyu)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We could also pass *SPUObject* to host directly. The magic is *sf.reveal*. And again, be careful in production!" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'deposit': array(990.25, dtype=float32), 'id': array(12345, dtype=int32)},\n", " {'deposit': array(99990.25, dtype=float32), 'id': array(12345, dtype=int32)}]" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sf.reveal(new_bank_account_spu)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Advanced Topic: Use Different MPC Protocol\n", "\n", "At this moment, SPU device supports multiple MPC protocol besides ABY3. It's easy to use different MPC protocol - just set the proper field in cluster def.\n", "\n", "For instance, if someone would like to use 2PC protocol - Cheetah,\n", "You should prepare another cluster def:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "import spu\n", "\n", "import secretflow as sf\n", "\n", "# In case you have a running secretflow runtime already.\n", "sf.shutdown()\n", "\n", "sf.init(['alice', 'bob', 'carol', 'dave'], address='local')\n", "\n", "cheetah_config = sf.utils.testing.cluster_def(\n", " parties=['alice', 'bob'],\n", " runtime_config={\n", " 'protocol': spu.spu_pb2.CHEETAH,\n", " 'field': spu.spu_pb2.FM64,\n", " },\n", ")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then you could create an SPU device with *cheetah_config*." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "spu_device2 = sf.SPU(cheetah_config)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's check the *cluster_def* of spu_device2." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'nodes': [{'party': 'alice', 'id': 'local:0', 'address': '127.0.0.1:56917'},\n", " {'party': 'bob', 'id': 'local:1', 'address': '127.0.0.1:27783'}],\n", " 'runtime_config': {'protocol': 4, 'field': 2}}" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "spu_device2.cluster_def\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We could use *spu_device2* to check famous Yao's Millionaires' problem." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "def get_carol_assets():\n", " return 1000000\n", "\n", "\n", "def get_dave_assets():\n", " return 1000002\n", "\n", "\n", "carol, dave = sf.PYU('carol'), sf.PYU('dave')\n", "\n", "carol_assets = carol(get_carol_assets)()\n", "dave_assets = dave(get_dave_assets)()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We use *spu_device2* to check if *carol* is richer." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\u001b[2m\u001b[36m(pid=924219)\u001b[0m 2022-08-30 18:34:54.138914: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /opt/rh/rh-ruby25/root/usr/local/lib64:/opt/rh/rh-ruby25/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib:/opt/rh/devtoolset-11/root/usr/lib64/dyninst:/opt/rh/devtoolset-11/root/usr/lib/dyninst\n", "\u001b[2m\u001b[36m(pid=924216)\u001b[0m 2022-08-30 18:34:54.138916: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /opt/rh/rh-ruby25/root/usr/local/lib64:/opt/rh/rh-ruby25/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib:/opt/rh/devtoolset-11/root/usr/lib64/dyninst:/opt/rh/devtoolset-11/root/usr/lib/dyninst\n", "\u001b[2m\u001b[36m(_run pid=924214)\u001b[0m 2022-08-30 18:34:54.837911: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /opt/rh/rh-ruby25/root/usr/local/lib64:/opt/rh/rh-ruby25/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib:/opt/rh/devtoolset-11/root/usr/lib64/dyninst:/opt/rh/devtoolset-11/root/usr/lib/dyninst\n", "\u001b[2m\u001b[36m(_run pid=924220)\u001b[0m 2022-08-30 18:34:54.830053: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /opt/rh/rh-ruby25/root/usr/local/lib64:/opt/rh/rh-ruby25/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib:/opt/rh/devtoolset-11/root/usr/lib64/dyninst:/opt/rh/devtoolset-11/root/usr/lib/dyninst\n", "\u001b[2m\u001b[36m(pid=924217)\u001b[0m 2022-08-30 18:34:54.790930: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /opt/rh/rh-ruby25/root/usr/local/lib64:/opt/rh/rh-ruby25/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib:/opt/rh/devtoolset-11/root/usr/lib64/dyninst:/opt/rh/devtoolset-11/root/usr/lib/dyninst\n", "\u001b[2m\u001b[36m(pid=924213)\u001b[0m 2022-08-30 18:34:54.790928: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /opt/rh/rh-ruby25/root/usr/local/lib64:/opt/rh/rh-ruby25/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib:/opt/rh/devtoolset-11/root/usr/lib64/dyninst:/opt/rh/devtoolset-11/root/usr/lib/dyninst\n", "\u001b[2m\u001b[36m(pid=924218)\u001b[0m 2022-08-30 18:34:54.790927: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /opt/rh/rh-ruby25/root/usr/local/lib64:/opt/rh/rh-ruby25/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib:/opt/rh/devtoolset-11/root/usr/lib64/dyninst:/opt/rh/devtoolset-11/root/usr/lib/dyninst\n", "\u001b[2m\u001b[36m(pid=924215)\u001b[0m 2022-08-30 18:34:54.790941: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /opt/rh/rh-ruby25/root/usr/local/lib64:/opt/rh/rh-ruby25/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib:/opt/rh/devtoolset-11/root/usr/lib64/dyninst:/opt/rh/devtoolset-11/root/usr/lib/dyninst\n", "\u001b[2m\u001b[36m(SPURuntime pid=924219)\u001b[0m I0830 18:34:55.906383 924219 external/com_github_brpc_brpc/src/brpc/server.cpp:1066] Server[yacl::link::internal::ReceiverServiceImpl] is serving on port=56917.\n", "\u001b[2m\u001b[36m(SPURuntime pid=924219)\u001b[0m I0830 18:34:55.906445 924219 external/com_github_brpc_brpc/src/brpc/server.cpp:1069] Check out http://k69b13338.eu95sqa:56917 in web browser.\n", "\u001b[2m\u001b[36m(SPURuntime pid=924216)\u001b[0m I0830 18:34:55.919610 924216 external/com_github_brpc_brpc/src/brpc/server.cpp:1066] Server[yacl::link::internal::ReceiverServiceImpl] is serving on port=27783.\n", "\u001b[2m\u001b[36m(SPURuntime pid=924216)\u001b[0m I0830 18:34:55.919660 924216 external/com_github_brpc_brpc/src/brpc/server.cpp:1069] Check out http://k69b13338.eu95sqa:27783 in web browser.\n", "\u001b[2m\u001b[36m(_run pid=924214)\u001b[0m 2022-08-30 18:34:56,578,578 WARNING [xla_bridge.py:backends:265] No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)\n", "\u001b[2m\u001b[36m(_run pid=924220)\u001b[0m 2022-08-30 18:34:56,654,654 WARNING [xla_bridge.py:backends:265] No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)\n" ] }, { "data": { "text/plain": [ "array(False)" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def get_winner(carol, dave):\n", " return carol > dave\n", "\n", "\n", "winner = spu_device2(get_winner)(carol_assets, dave_assets)\n", "\n", "sf.reveal(winner)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Advanced Topic: Multiple Returns from SPU Computation\n", "\n", "In most cases, we have multiple returns from the function executed by SPU device.\n", "\n", "For instance," ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "def get_multiple_outputs(x, y):\n", " return x + y, x - y\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are multiple options to handle this.\n", "\n", "### Option 1: Treat All Returns as Single\n", "\n", "This is the default behavior of SPU. Let's see." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "single_output = spu_device2(get_multiple_outputs)(carol_assets, dave_assets)\n", "\n", "single_output\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We could see we only get a single *SPUObject*. Let's reveal it." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(array(2000002, dtype=int32), array(-2, dtype=int32))" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sf.reveal(single_output)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So single_output itself actually represents a tuple." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Option 2: Decide Return Nums on the Fly\n", "\n", "We can also instruct SPU to decide return numbers for us." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(,\n", " )" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from secretflow.device.device.spu import SPUCompilerNumReturnsPolicy\n", "\n", "multiple_outputs = spu_device2(\n", " get_multiple_outputs, num_returns_policy=SPUCompilerNumReturnsPolicy.FROM_COMPILER\n", ")(carol_assets, dave_assets)\n", "\n", "multiple_outputs\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "let's check two outputs respectively." ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2000002\n", "-2\n" ] } ], "source": [ "print(sf.reveal(multiple_outputs[0]))\n", "print(sf.reveal(multiple_outputs[1]))\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Option 3: Decide Return Nums Manually\n", "\n", "If possible, you could also set return nums manually." ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[,\n", " ]" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "user_multiple_outputs = spu_device2(\n", " get_multiple_outputs,\n", " num_returns_policy=SPUCompilerNumReturnsPolicy.FROM_USER,\n", " user_specified_num_returns=2,\n", ")(carol_assets, dave_assets)\n", "\n", "user_multiple_outputs\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "let's also check two outputs respectively." ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2000002\n", "-2\n" ] } ], "source": [ "print(sf.reveal(multiple_outputs[0]))\n", "print(sf.reveal(multiple_outputs[1]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's summarize what we have:\n", "\n", "- Be default, SPU treats all the returns as a single return\n", "- Since SPU compiler generates the SPU executable, it can figure out return nums. However, the options results some latency since we have to make compilation blocked.\n", "- If you want to avoid latency, we can provide return nums manually. But you have to make sure you provide the right nums, otherwise, the program would complain!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## What's Next?\n", "\n", "After learning basics of SPU, you may check some advanced tutorials with SPU:\n", "\n", "- [Logistic Regression with SPU](./lr_with_spu.ipynb)\n", "- [Neural Network with SPU](./nn_with_spu.ipynb)\n" ] } ], "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.13 (default, Mar 28 2022, 11:38:47) \n[GCC 7.5.0]" }, "orig_nbformat": 4, "vscode": { "interpreter": { "hash": "de4c945f5346493decaa0ea82289843a7da2415616b96b9f4b104111cc0c19ad" } } }, "nbformat": 4, "nbformat_minor": 2 }