Thuật toán liên kết tùy chỉnh, Phần 1: Giới thiệu về lõi liên kết

Xem trên TensorFlow.org Chạy trong Google Colab Xem nguồn trên GitHub Tải xuống sổ ghi chép

Hướng dẫn này là phần đầu tiên của một loạt bài gồm hai phần đó cho thấy làm thế nào để thực hiện các loại tùy chỉnh các thuật toán liên trong TensorFlow Federated (TFF) sử dụng Federated Core (FC) - một tập hợp các giao diện cấp thấp phục vụ như là một nền tảng mà trên đó chúng tôi đã triển khai Federated Learning (FL) lớp.

Phần đầu tiên này mang tính khái niệm nhiều hơn; chúng tôi giới thiệu một số khái niệm chính và lập trình trừu tượng được sử dụng trong TFF và chúng tôi chứng minh việc sử dụng chúng trên một ví dụ rất đơn giản với một dãy phân tán các cảm biến nhiệt độ. Trong phần thứ hai của loạt bài này , chúng tôi sử dụng các cơ chế chúng tôi giới thiệu ở đây để thực hiện một phiên bản đơn giản của đào tạo và đánh giá thuật toán liên. Là một theo dõi, chúng tôi khuyên bạn để nghiên cứu việc thực hiện của trung bình liên trong tff.learning .

Đến cuối loạt bài này, bạn sẽ có thể nhận ra rằng các ứng dụng của Federated Core không nhất thiết phải giới hạn trong việc học. Các phần tóm tắt lập trình mà chúng tôi cung cấp khá chung chung và có thể được sử dụng, chẳng hạn như để triển khai phân tích và các loại tính toán tùy chỉnh khác trên dữ liệu phân tán.

Mặc dù hướng dẫn này được thiết kế để khép kín, chúng tôi khuyên bạn để hướng dẫn đọc đầu tiên trên phân loại hình ảnhthế hệ văn bản cho một cấp cao hơn và giới thiệu nhẹ nhàng hơn đối với khuôn khổ TensorFlow Federated và Federated Learning API ( tff.learning ), như nó sẽ giúp bạn đặt các khái niệm mà chúng tôi mô tả ở đây vào ngữ cảnh.

Mục đích sử dụng

Tóm lại, Federated Core (FC) là một môi trường phát triển mà làm cho nó có thể để gọn thể hiện logic chương trình liên hợp gặt đập TensorFlow mã với các nhà khai thác phân phối truyền thông, chẳng hạn như những người được sử dụng trong Federated trung bình - tính toán khoản tiền phân phối, trung bình, và các loại khác tổng hợp phân tán trên một tập hợp các thiết bị khách trong hệ thống, phát các mô hình và thông số đến các thiết bị đó, v.v.

Bạn có thể nhận thức được tf.contrib.distribute , và một câu hỏi tự nhiên để hỏi vào thời điểm này có thể là: trong những cách không khuôn khổ này khác nhau? Xét cho cùng, cả hai khung công tác đều cố gắng làm cho các phép tính TensorFlow được phân phối.

Một cách để suy nghĩ về nó là, trong khi mục tiêu đề ra của tf.contrib.distributecho phép người dùng sử dụng mô hình hiện có và mã đào tạo với những thay đổi nhỏ để cho phép đào tạo phân phối, và nhiều tập trung vào làm thế nào để tận dụng cơ sở hạ tầng phân phối để làm cho mã đào tạo hiện có hiệu quả hơn, mục tiêu của Lõi liên kết của TFF là cung cấp cho các nhà nghiên cứu và học viên quyền kiểm soát rõ ràng đối với các mẫu truyền thông phân tán cụ thể mà họ sẽ sử dụng trong hệ thống của mình. Trọng tâm trong FC là cung cấp một ngôn ngữ linh hoạt và có thể mở rộng để thể hiện các thuật toán luồng dữ liệu phân tán, chứ không phải là một tập hợp cụ thể các khả năng đào tạo phân tán được thực hiện.

Một trong những đối tượng mục tiêu chính cho API FC của TFF là các nhà nghiên cứu và học viên có thể muốn thử nghiệm các thuật toán học tập liên hợp mới và đánh giá hậu quả của các lựa chọn thiết kế tinh vi ảnh hưởng đến cách thức điều phối luồng dữ liệu trong hệ thống phân tán. mà không bị sa lầy bởi các chi tiết triển khai hệ thống. Mức độ trừu tượng mà FC API đang hướng tới gần tương ứng với mã giả mà người ta có thể sử dụng để mô tả cơ chế của thuật toán học liên hợp trong một ấn phẩm nghiên cứu - dữ liệu nào tồn tại trong hệ thống và cách nó được chuyển đổi, nhưng không giảm xuống mức trao đổi tin nhắn mạng điểm-điểm cá nhân.

TFF nói chung là nhắm mục tiêu các tình huống trong đó dữ liệu được phân phối và phải duy trì như vậy, ví dụ: vì lý do bảo mật và nơi thu thập tất cả dữ liệu tại một địa điểm tập trung có thể không phải là một lựa chọn khả thi. Điều này có nghĩa là về việc triển khai các thuật toán học máy đòi hỏi mức độ kiểm soát rõ ràng hơn, so với các tình huống trong đó tất cả dữ liệu có thể được tích lũy ở một vị trí tập trung tại một trung tâm dữ liệu.

Trước khi chúng ta bắt đầu

Trước khi chúng tôi đi sâu vào mã, hãy thử chạy ví dụ "Hello World" sau đây để đảm bảo rằng môi trường của bạn được thiết lập chính xác. Nếu nó không hoạt động, vui lòng tham khảo các cài đặt hướng dẫn để được hướng dẫn.

!pip install --quiet --upgrade tensorflow-federated-nightly
!pip install --quiet --upgrade nest-asyncio

import nest_asyncio
nest_asyncio.apply()
import collections

import numpy as np
import tensorflow as tf
import tensorflow_federated as tff
@tff.federated_computation
def hello_world():
  return 'Hello, World!'

hello_world()
b'Hello, World!'

Dữ liệu liên kết

Một trong những tính năng nổi bật của TFF là nó cho phép bạn gọn thể hiện tính TensorFlow-dựa trên dữ liệu liên. Chúng tôi sẽ sử dụng các dữ liệu liên hạn trong hướng dẫn này để đề cập đến một bộ sưu tập của các mục dữ liệu được tổ chức trên một nhóm các thiết bị trong một hệ thống phân phối. Ví dụ: các ứng dụng chạy trên thiết bị di động có thể thu thập dữ liệu và lưu trữ cục bộ mà không cần tải lên vị trí tập trung. Hoặc, một loạt các cảm biến phân tán có thể thu thập và lưu trữ các kết quả đo nhiệt độ tại vị trí của chúng.

Dữ liệu Federated như những người trong các ví dụ trên được điều trị tại TFF như công dân hạng nhất , tức là, họ có thể xuất hiện như là các thông số và kết quả của các chức năng, và họ có các loại. Để củng cố khái niệm này, chúng tôi sẽ đề cập đến các bộ dữ liệu liên như các giá trị liên kết, hoặc như các giá trị của các loại liên.

Điểm quan trọng cần hiểu là chúng tôi đang lập mô hình toàn bộ bộ sưu tập các mục dữ liệu trên tất cả các thiết bị (ví dụ: toàn bộ số đọc nhiệt độ bộ sưu tập từ tất cả các cảm biến trong một mảng phân tán) dưới dạng một giá trị liên kết duy nhất.

Ví dụ, đây là cách người ta sẽ xác định trong TFF loại một phao liên tổ chức bởi một nhóm các thiết bị của khách hàng. Một tập hợp các số đọc nhiệt độ hiện thực hóa trên một loạt các cảm biến phân tán có thể được mô hình hóa như một giá trị của loại liên hợp này.

federated_float_on_clients = tff.type_at_clients(tf.float32)

Tổng quát hơn, một loại liên trong TFF được xác định bằng cách xác định loại T của các thành viên của nó - các hạng mục dữ liệu mà cư trú trên các thiết bị cá nhân, và các nhóm G của các thiết bị trên đó các giá trị liên thuộc loại này được lưu trữ (cộng với một phần ba, thông tin tùy chọn mà chúng tôi sẽ đề cập ngay sau đây). Chúng tôi tham khảo các nhóm G của các thiết bị lưu trữ một giá trị liên như vị trí của giá trị. Như vậy, tff.CLIENTS là một ví dụ về một vị trí.

str(federated_float_on_clients.member)
'float32'
str(federated_float_on_clients.placement)
'CLIENTS'

Một loại liên với các thành viên T và vị trí G có thể được biểu gọn như {T}@G , như hình dưới đây.

str(federated_float_on_clients)
'{float32}@CLIENTS'

Các dấu ngoặc nhọn {} một cách cô đọng này ký hiệu đóng vai trò như một lời nhắc nhở rằng các thành viên (các hạng mục dữ liệu trên các thiết bị khác nhau) có thể khác nhau, như bạn mong chờ ví dụ, các bài đọc cảm biến nhiệt độ, do đó khách hàng như một nhóm đang cùng nhau tổ chức một đa -Thiết của T -typed mục mà cùng nhau tạo thành giá trị liên.

Điều quan trọng cần lưu ý là các thành viên của một giá trị liên thường đục để các lập trình viên, tức là một giá trị liên không nên được coi là một đơn giản dict keyed bởi một định danh của một thiết bị trong hệ thống - những giá trị này được dùng để được chuyển đổi chung chỉ bằng cách khai thác liên mà trừu tượng đại diện cho các loại giao thức truyền thông phân phối (ví dụ như tập hợp). Nếu điều này nghe có vẻ quá trừu tượng, đừng lo lắng - chúng tôi sẽ quay lại vấn đề này ngay sau đó và chúng tôi sẽ minh họa nó bằng các ví dụ cụ thể.

Các loại liên kết trong TFF có hai loại: loại mà các thành phần thành viên của một giá trị liên hợp có thể khác nhau (như vừa thấy ở trên) và loại mà chúng được biết là tất cả đều bình đẳng. Đây được điều khiển bởi thứ ba, tùy chọn all_equal tham số trong tff.FederatedType constructor (mặc định cho False ).

federated_float_on_clients.all_equal
False

Một loại liên với một vị trí G trong đó tất cả các T -typed thành viên được biết đến là bình đẳng có thể được biểu diễn dưới dạng gọn T@G (như trái ngược với {T}@G , có nghĩa là, với các dấu ngoặc nhọn giảm để phản ánh thực tế là tập hợp nhiều thành phần cấu thành bao gồm một mục duy nhất).

str(tff.type_at_clients(tf.float32, all_equal=True))
'float32@CLIENTS'

Một ví dụ về giá trị được liên kết của loại như vậy có thể phát sinh trong các tình huống thực tế là một siêu thông số (chẳng hạn như tốc độ học tập, chỉ tiêu cắt, v.v.) đã được máy chủ truyền phát tới một nhóm thiết bị tham gia đào tạo liên kết.

Một ví dụ khác là một tập hợp các tham số cho mô hình học máy được đào tạo trước tại máy chủ, sau đó được truyền tới một nhóm thiết bị khách, nơi chúng có thể được cá nhân hóa cho từng người dùng.

Ví dụ, giả sử chúng ta có một cặp float32 thông số ab cho một mô hình hồi quy tuyến tính đơn giản một chiều. Chúng ta có thể xây dựng kiểu (không liên kết) của các mô hình như vậy để sử dụng trong TFF như sau. Các dấu ngoặc góc <> trong chuỗi kiểu in là một ký hiệu TFF nhỏ gọn cho các bộ tên hoặc không có tên.

simple_regression_model_type = (
    tff.StructType([('a', tf.float32), ('b', tf.float32)]))

str(simple_regression_model_type)
'<a=float32,b=float32>'

Lưu ý rằng chúng tôi chỉ xác định dtype s trên. Các loại không vô hướng cũng được hỗ trợ. Trong đoạn mã trên, tf.float32 là một ký hiệu phím tắt cho tổng quát hơn tff.TensorType(dtype=tf.float32, shape=[]) .

Khi mô hình này được phát cho khách hàng, loại giá trị liên kết kết quả có thể được biểu diễn như hình dưới đây.

str(tff.type_at_clients(
    simple_regression_model_type, all_equal=True))
'<a=float32,b=float32>@CLIENTS'

Mỗi đối xứng với phao liên trên, chúng tôi sẽ đề cập đến một loại như một tuple liên. Tổng quát hơn, chúng ta thường sẽ sử dụng XYZ liên thuật ngữ để nói đến một giá trị liên, trong đó thành phần thành viên là XYZ -like. Do đó, chúng ta sẽ nói về những thứ như các bộ liên kết, chuỗi liên kết, mô hình liên kết, và vân vân.

Bây giờ, quay trở lại để float32@CLIENTS - trong khi nó xuất hiện nhân rộng trên nhiều thiết bị, nó thực sự là một đơn float32 , vì tất cả thành viên đều giống nhau. Nói chung, bạn có thể nghĩ ra bất kỳ loại liên hoàn toàn bình đẳng, tức là, một trong những hình thức T@G , như đẳng cấu với một loại phi liên T , vì trong cả hai trường hợp, có thực sự chỉ có một (mặc dù có khả năng nhân rộng) mục kiểu T .

Với đẳng cấu giữa TT@G , bạn có thể tự hỏi mục đích gì, nếu có, các loại sau có thể phục vụ. Đọc tiếp.

Vị trí

Tổng quan thiết kế

Trong phần trước, chúng tôi đã giới thiệu các khái niệm về vị trí - nhóm người tham gia hệ thống mà có thể cùng nhau tổ chức một giá trị liên kết, và chúng tôi đã chứng minh việc sử dụng tff.CLIENTS là một ví dụ đặc điểm kỹ thuật của một vị trí.

Để giải thích tại sao khái niệm về một vị trí rất cơ bản mà chúng tôi cần phải kết hợp nó vào hệ thống kiểu TFF, nhớ lại những gì chúng tôi đề cập ở đầu của hướng dẫn này về một số mục đích sử dụng của TFF.

Mặc dù trong hướng dẫn này, bạn sẽ chỉ thấy mã TFF được thực thi cục bộ trong môi trường mô phỏng, mục tiêu của chúng tôi là TFF cho phép viết mã mà bạn có thể triển khai để thực thi trên các nhóm thiết bị vật lý trong hệ thống phân tán, có khả năng bao gồm thiết bị di động hoặc thiết bị nhúng chạy Android. Mỗi thiết bị trong số đó sẽ nhận được một bộ hướng dẫn riêng biệt để thực thi cục bộ, tùy thuộc vào vai trò của nó trong hệ thống (thiết bị người dùng cuối, bộ điều phối tập trung, lớp trung gian trong kiến ​​trúc nhiều tầng, v.v.). Điều quan trọng là có thể suy luận về việc tập hợp con nào của thiết bị thực thi mã nào và nơi các phần dữ liệu khác nhau có thể hiện thực hóa về mặt vật lý.

Điều này đặc biệt quan trọng khi xử lý, ví dụ: dữ liệu ứng dụng trên thiết bị di động. Vì dữ liệu là riêng tư và có thể nhạy cảm, chúng tôi cần khả năng xác minh tĩnh rằng dữ liệu này sẽ không bao giờ rời khỏi thiết bị (và chứng minh sự thật về cách dữ liệu đang được xử lý). Các thông số kỹ thuật về vị trí là một trong những cơ chế được thiết kế để hỗ trợ điều này.

TFF đã được thiết kế như một môi trường lập trình dữ liệu trung tâm, và như vậy, không giống như một số các khuôn khổ hiện tại mà tập trung vào các hoạt động và nơi những hoạt động có thể chạy, TFF tập trung vào dữ liệu, nơi mà materializes dữ liệu, và làm thế nào nó được biến đổi. Do đó, vị trí được mô hình hóa như một thuộc tính của dữ liệu trong TFF, chứ không phải là một thuộc tính của các hoạt động trên dữ liệu. Thật vậy, như bạn sắp thấy trong phần tiếp theo, một số hoạt động TFF trải dài khắp các địa điểm và chạy "trong mạng", có thể nói, thay vì được thực thi bởi một máy hoặc một nhóm máy.

Đại diện cho các loại một giá trị nhất định như T@G hoặc {T}@G (như trái ngược với chỉ T ) đưa ra quyết định vị trí dữ liệu rõ ràng, và cùng với một phân tích tĩnh của các chương trình viết bằng TFF, nó có thể đóng vai trò như một nền tảng cho việc cung cấp đảm bảo quyền riêng tư chính thức cho dữ liệu nhạy cảm trên thiết bị.

Một điều quan trọng cần lưu ý vào thời điểm này, tuy nhiên, đó là trong khi chúng tôi khuyến khích người dùng TFF để được rõ ràng về các nhóm thiết bị tham gia rằng máy chủ dữ liệu (các vị trí), các lập trình viên sẽ không bao giờ đối phó với các dữ liệu thô hoặc nhận dạng của những người tham gia cá nhân .

Trong cơ thể của mã TFF, do thiết kế, không có cách nào để liệt kê các thiết bị tạo thành nhóm đại diện bởi tff.CLIENTS , hoặc để thăm dò đối với sự tồn tại của một thiết bị cụ thể trong nhóm. Không có khái niệm về thiết bị hoặc danh tính khách hàng ở bất kỳ đâu trong API cốt lõi liên kết, tập hợp cơ bản của các kiến ​​trúc trừu tượng hoặc cơ sở hạ tầng thời gian chạy cốt lõi mà chúng tôi cung cấp để hỗ trợ mô phỏng. Tất cả logic tính toán bạn viết sẽ được thể hiện dưới dạng các hoạt động trên toàn bộ nhóm khách hàng.

Nhớ lại đây những gì chúng ta đã đề cập trước về giá trị của các loại liên là không giống như Python dict , trong một có thể không chỉ đơn giản là liệt kê các thành phần thành viên của họ. Hãy nghĩ về các giá trị mà logic chương trình TFF của bạn thao tác như được liên kết với các vị trí (nhóm), thay vì với những người tham gia riêng lẻ.

Vị trí được thiết kế để trở thành một công dân hạng nhất trong TFF là tốt, và có thể xuất hiện như là các thông số và kết quả của một placement loại (được đại diện bởi tff.PlacementType trong API). Trong tương lai, chúng tôi có kế hoạch cung cấp nhiều toán tử khác nhau để chuyển đổi hoặc kết hợp các vị trí, nhưng điều này nằm ngoài phạm vi của hướng dẫn này. Còn bây giờ, nó cũng đủ để nghĩ về placement như một đục nguyên thủy được xây dựng-in gõ TFF, tương tự như cách intbool được đục sẵn trong các loại bằng Python, với tff.CLIENTS là một chữ liên tục của loại hình này, không khác gì 1 là một chữ liên tục kiểu int .

Chỉ định vị trí

TFF cung cấp hai literals vị trí cơ bản, tff.CLIENTStff.SERVER , để làm cho nó dễ dàng để thể hiện sự đa dạng phong phú của các kịch bản thực tế được tự nhiên mô hình hóa như kiến trúc client-server, với nhiều thiết bị khách hàng (điện thoại di động, các thiết bị nhúng, cơ sở dữ liệu phân tán , cảm biến, vv) dàn dựng bởi một điều phối viên máy chủ tập trung duy nhất. TFF được thiết kế để cũng hỗ trợ các vị trí tùy chỉnh, nhiều nhóm khách hàng, nhiều tầng và các kiến ​​trúc phân tán tổng quát hơn, nhưng thảo luận về chúng nằm ngoài phạm vi của hướng dẫn này.

TFF không quy định gì hoặc là tff.CLIENTS hoặc tff.SERVER thực sự đại diện.

Đặc biệt, tff.SERVER có thể là một thiết bị vật lý duy nhất (một thành viên của một nhóm singleton), nhưng nó cũng giống như cũng có thể là một nhóm các bản sao trong một cụm fault-tolerant chạy sao chép máy nhà nước - chúng tôi không thực hiện bất kỳ kiến trúc đặc biệt các giả định. Thay vào đó, chúng tôi sử dụng all_equal chút đề cập trong phần trước để bày tỏ một thực tế mà chúng ta đang nói chung đối phó với chỉ một mục duy nhất của dữ liệu tại máy chủ.

Tương tự như vậy, tff.CLIENTS trong một số ứng dụng có thể đại diện cho tất cả khách hàng trong hệ thống - những gì trong bối cảnh học tập liên đôi khi chúng ta gọi là dân số, nhưng ví dụ, trong việc triển khai sản xuất Federated trung bình , nó có thể đại diện cho một nhóm - một tập hợp con của các khách hàng được chọn để tham gia vào một đợt đào tạo cụ thể. Các vị trí được xác định trừu tượng có ý nghĩa cụ thể khi một phép tính mà chúng xuất hiện được triển khai để thực thi (hoặc đơn giản được gọi như một hàm Python trong môi trường mô phỏng, như được minh họa trong hướng dẫn này). Trong mô phỏng cục bộ của chúng tôi, nhóm khách hàng được xác định bởi dữ liệu liên kết được cung cấp làm đầu vào.

Tính toán liên hợp

Khai báo các phép tính liên hợp

TFF được thiết kế như một môi trường lập trình chức năng được đánh máy mạnh hỗ trợ phát triển mô-đun.

Các đơn vị cơ bản của thành phần trong TFF là một tính toán liên - một phần của logic mà có thể chấp nhận các giá trị liên như đầu vào và trả về giá trị liên như đầu ra. Đây là cách bạn có thể xác định một phép tính tính trung bình của nhiệt độ được báo cáo bởi mảng cảm biến từ ví dụ trước của chúng tôi.

@tff.federated_computation(tff.type_at_clients(tf.float32))
def get_average_temperature(sensor_readings):
  return tff.federated_mean(sensor_readings)

Nhìn vào các mã trên, vào thời điểm này bạn có thể được yêu cầu - không có đã trang trí cấu trúc để xác định đơn vị composable như tf.function trong TensorFlow, và nếu như vậy, tại sao giới thiệu chưa nhau, và làm thế nào có gì khác?

Câu trả lời ngắn gọn là các mã được tạo bởi các tff.federated_computation wrapper không phải là TensorFlow, cũng không phải là Python - đó là một đặc điểm kỹ thuật của một hệ thống phân phối trong một nền tảng độc lập ngôn ngữ keo nội bộ. Tại thời điểm này, điều này chắc chắn sẽ nghe có vẻ khó hiểu, nhưng hãy lưu ý cách giải thích trực quan này về phép tính liên hợp như một đặc điểm kỹ thuật trừu tượng của hệ thống phân tán trong tâm trí. Chúng tôi sẽ giải thích nó trong một phút.

Đầu tiên, chúng ta hãy chơi với định nghĩa một chút. Các phép tính TFF thường được mô hình hóa dưới dạng các hàm - có hoặc không có tham số, nhưng với các ký hiệu kiểu được xác định rõ ràng. Bạn có thể in các loại chữ ký của một tính toán bằng cách truy vấn của nó type_signature tài sản, như hình dưới đây.

str(get_average_temperature.type_signature)
'({float32}@CLIENTS -> float32@SERVER)'

Chữ ký kiểu cho chúng ta biết rằng phép tính chấp nhận một bộ sưu tập các bài đọc cảm biến khác nhau trên các thiết bị khách và trả về một mức trung bình trên máy chủ.

Trước khi chúng tôi đi thêm nữa, chúng ta hãy suy nghĩ về điều này trong một phút - Đầu vào và đầu ra của tính toán này là ở những nơi khác nhau (trên CLIENTS vs tại SERVER ). Nhớ lại những gì chúng tôi đã nói ở phần trước trên các vị trí về cách thức hoạt động TFF có thể chiều dài qua địa điểm, và chạy trong mạng, và những gì chúng ta vừa nói về tính liên như đại diện thông số kỹ thuật trừu tượng của hệ thống phân phối. Chúng tôi vừa xác định một tính toán như vậy - một hệ thống phân tán đơn giản, trong đó dữ liệu được sử dụng tại các thiết bị khách và kết quả tổng hợp xuất hiện tại máy chủ.

Trong nhiều tình huống thực tế, các tính toán mà đại diện cho các nhiệm vụ cấp cao nhất sẽ có xu hướng chấp nhận đầu vào của họ và báo cáo kết quả đầu ra của họ tại máy chủ - điều này phản ánh ý kiến cho rằng tính toán có thể được kích hoạt bởi các truy vấn có nguồn gốc và kết thúc trên máy chủ.

Tuy nhiên, FC API không áp đặt giả thiết này, và nhiều người trong các khối xây dựng chúng tôi sử dụng trong nội bộ (bao gồm nhiều tff.federated_... khai thác bạn có thể thấy trong API) có đầu vào và đầu ra với các vị trí khác nhau, vì vậy nói chung, bạn nên không suy nghĩ về một tính toán liên như cái gì mà chạy trên máy chủ hoặc được thực hiện bởi một máy chủ. Máy chủ chỉ là một loại người tham gia trong tính toán liên hợp. Khi suy nghĩ về cơ chế của những tính toán như vậy, tốt nhất bạn nên luôn mặc định theo quan điểm mạng toàn cầu, thay vì quan điểm của một điều phối viên tập trung duy nhất.

Nói chung, chữ ký loại chức năng được biểu diễn dưới dạng gọn (T -> U) với nhiều loại TU đầu vào và đầu ra, tương ứng. Các kiểu của tham số chính thức (ví dụ sensor_readings trong trường hợp này) được quy định như là đối số cho trang trí. Bạn không cần chỉ định loại kết quả - nó được xác định tự động.

Mặc dù TFF cung cấp các dạng đa hình hạn chế, nhưng các lập trình viên được khuyến khích nên trình bày rõ ràng về các loại dữ liệu mà họ làm việc, vì điều đó làm cho việc hiểu, gỡ lỗi và xác minh chính thức các thuộc tính mã của bạn dễ dàng hơn. Trong một số trường hợp, chỉ định rõ ràng các kiểu là một yêu cầu (ví dụ: các phép tính đa hình hiện không thể thực thi trực tiếp).

Thực thi các phép tính liên hợp

Để hỗ trợ phát triển và gỡ lỗi, TFF cho phép bạn gọi trực tiếp các phép tính được định nghĩa theo cách này dưới dạng các hàm Python, như được hiển thị bên dưới. Trong trường hợp tính toán dự đoán một giá trị của một loại liên với all_equal bộ chút để False , bạn có thể ăn nó như là một đồng bằng list bằng Python, và với nhiều loại liên với all_equal set bit True , bạn có thể chỉ trực tiếp nuôi dưỡng (duy nhất) thành viên cấu thành. Đây cũng là cách kết quả được thông báo lại cho bạn.

get_average_temperature([68.5, 70.3, 69.8])
69.53334

Khi chạy các tính toán như thế này trong chế độ mô phỏng, bạn đóng vai trò là người quan sát bên ngoài với chế độ xem toàn hệ thống, người có khả năng cung cấp đầu vào và tiêu thụ đầu ra tại bất kỳ vị trí nào trong mạng, như thực tế là trường hợp ở đây - bạn đã cung cấp các giá trị máy khách ở đầu vào và sử dụng kết quả máy chủ.

Bây giờ, chúng ta hãy quay trở lại một lưu ý chúng tôi thực hiện trước đó về tff.federated_computation trang trí phát ra mã trong một ngôn ngữ keo. Mặc dù logic của tính toán TFF có thể được thể hiện dưới dạng các chức năng bình thường trong Python (bạn chỉ cần trang trí chúng với tff.federated_computation như chúng tôi đã làm ở trên), và bạn có thể trực tiếp gọi họ với các đối số Python giống như bất kỳ chức năng Python khác trong này máy tính xách tay, đằng sau hậu trường, như chúng ta đã nói trước đó, tính toán TFF là thực sự không Python.

Những gì chúng ta ý nghĩa của điều này là khi trình thông dịch Python gặp một chức năng được trang trí với tff.federated_computation , nó dấu vết những điều khoản trong cơ thể của chức năng này một lần (tại thời điểm định nghĩa), và sau đó xây dựng một đại diện tuần tự logic của tính toán để sử dụng trong tương lai - cho dù để thực thi, hoặc được kết hợp như một thành phần phụ vào một phép tính khác.

Bạn có thể xác minh điều này bằng cách thêm một câu lệnh in, như sau:

@tff.federated_computation(tff.type_at_clients(tf.float32))
def get_average_temperature(sensor_readings):

  print ('Getting traced, the argument is "{}".'.format(
      type(sensor_readings).__name__))

  return tff.federated_mean(sensor_readings)
Getting traced, the argument is "ValueImpl".

Bạn có thể nghĩ về mã Python xác định một phép tính liên kết tương tự như cách bạn nghĩ về mã Python xây dựng biểu đồ TensorFlow trong bối cảnh không háo hức (nếu bạn không quen với các cách sử dụng không háo hức của TensorFlow, hãy nghĩ về Mã Python xác định một biểu đồ của các hoạt động sẽ được thực thi sau này, nhưng không thực sự chạy chúng một cách nhanh chóng). Mã xây dựng đồ thị không háo hức trong TensorFlow là Python, nhưng biểu đồ TensorFlow được xây dựng bởi mã này độc lập với nền tảng và có thể tuần tự hóa.

Tương tự như vậy, tính toán TFF được định nghĩa trong Python, nhưng những điều khoản Python trong cơ thể của họ, chẳng hạn như tff.federated_mean trong ví dụ weve chỉ hiển thị, được biên soạn thành một đại diện serializable di động và nền tảng độc lập dưới mui xe.

Là một nhà phát triển, bạn không cần phải quan tâm đến các chi tiết của bản trình bày này, vì bạn sẽ không bao giờ cần trực tiếp làm việc với nó, nhưng bạn nên biết về sự tồn tại của nó, thực tế là các phép tính TFF về cơ bản là không cần thiết, và không thể nắm bắt trạng thái Python tùy ý. Mã Python chứa trong cơ thể của một TFF tính toán được thực hiện vào thời điểm xác định, khi các cơ quan chức năng Python trang trí với tff.federated_computation được bắt nguồn từ trước khi bắt đăng. Nó không được thực hiện lại vào thời điểm gọi (ngoại trừ khi hàm là đa hình; vui lòng tham khảo các trang tài liệu để biết thêm chi tiết).

Bạn có thể thắc mắc tại sao chúng tôi lại chọn giới thiệu một biểu diễn nội bộ chuyên dụng không phải Python. Một lý do là cuối cùng, các tính toán TFF nhằm mục đích có thể triển khai cho các môi trường vật lý thực và được lưu trữ trên thiết bị di động hoặc thiết bị nhúng, nơi Python có thể không khả dụng.

Một lý do khác là các tính toán TFF thể hiện hành vi toàn cầu của các hệ thống phân tán, trái ngược với các chương trình Python thể hiện hành vi cục bộ của từng người tham gia. Bạn có thể thấy rằng trong các ví dụ đơn giản trên, với các nhà điều hành đặc biệt tff.federated_mean chấp nhận dữ liệu trên các thiết bị của khách hàng, nhưng tiền gửi kết quả trên máy chủ.

Nhà điều hành tff.federated_mean không thể dễ dàng mô hình hóa như một nhà điều hành bình thường trong Python, vì nó không thực hiện tại địa phương - như đã nói ở trên, nó đại diện cho một hệ thống phân phối mà tọa độ hành vi của nhiều người tham gia hệ thống. Chúng tôi sẽ giới thiệu đến các nhà khai thác như các nhà khai thác liên kết, để phân biệt với bình thường khai thác (địa phương) trong Python.

Hệ thống kiểu TFF và tập hợp hoạt động cơ bản được hỗ trợ trong ngôn ngữ của TFF, do đó, khác biệt đáng kể so với hệ thống trong Python, yêu cầu sử dụng một biểu diễn chuyên dụng.

Soạn các phép tính liên hợp

Như đã lưu ý ở trên, các phép tính liên hợp và các thành phần của chúng được hiểu rõ nhất là các mô hình của hệ thống phân tán và bạn có thể nghĩ việc soạn các phép tính liên hợp giống như việc soạn các hệ thống phân tán phức tạp hơn từ các hệ thống đơn giản hơn. Bạn có thể nghĩ về tff.federated_mean điều hành như một loại tích hợp trong mẫu tính toán liên với một loại chữ ký ({T}@CLIENTS -> T@SERVER) (thực sự, giống như tính toán bạn viết, toán tử này cũng có một phức tạp cấu trúc - dưới mui xe, chúng tôi chia nhỏ nó thành các toán tử đơn giản hơn).

Điều này cũng đúng với việc soạn các phép tính liên hợp. Các tính toán get_average_temperature có thể được áp dụng trong một cơ thể của một hàm Python trang trí với tff.federated_computation - làm như vậy sẽ gây ra nó được nhúng vào trong cơ thể của phụ huynh, nhiều trong cùng một cách tff.federated_mean được nhúng vào trong cơ thể của chính mình trước đó.

Một hạn chế quan trọng là nhận thức được rằng các cơ quan chức năng Python trang trí với tff.federated_computation chỉ được bao gồm các nhà khai thác liên kết, ví dụ, họ có thể không trực tiếp chứa các hoạt động TensorFlow. Ví dụ, bạn có thể không trực tiếp sử dụng tf.nest giao diện để thêm một cặp giá trị liên. Đang TensorFlow phải được giới hạn trong các khối mã trang trí với một tff.tf_computation thảo luận trong phần sau. Chỉ khi quấn theo cách này mã TensorFlow bọc có thể được áp dụng trong cơ thể của một tff.federated_computation .

Những lý do để tách này là kỹ thuật (thật khó để đánh lừa các nhà khai thác như tf.add để làm việc với những người không tensors) cũng như kiến trúc. Ngôn ngữ của tính liên (ví dụ, logic được xây dựng từ cơ quan đăng các chức năng Python trang trí với tff.federated_computation ) được thiết kế để phục vụ như một ngôn ngữ keo nền tảng độc lập. Ngôn ngữ keo này hiện được sử dụng để xây dựng hệ thống phân phối từ phần nhúng mã TensorFlow (giới hạn trong tff.tf_computation khối). Trong sự viên mãn của thời gian, chúng tôi dự kiến nhu cầu phần nhúng của khác, không TensorFlow logic, chẳng hạn như truy vấn cơ sở dữ liệu quan hệ mà có thể đại diện cho đường ống đầu vào, tất cả các kết nối với nhau bằng cách sử dụng cùng một ngôn ngữ keo (các tff.federated_computation khối).

Logic TensorFlow

Khai báo tính toán TensorFlow

TFF được thiết kế để sử dụng với TensorFlow. Do đó, phần lớn mã bạn sẽ viết trong TFF có thể là mã TensorFlow thông thường (tức là thực thi cục bộ). Để sử dụng mã như vậy với TFF, như đã nói ở trên, nó chỉ cần được trang trí với tff.tf_computation .

Ví dụ, dưới đây là cách chúng ta có thể thực hiện một chức năng mà phải mất một số và thêm 0.5 đến nó.

@tff.tf_computation(tf.float32)
def add_half(x):
  return tf.add(x, 0.5)

Một lần nữa, nhìn vào điều này, bạn có thể tự hỏi tại sao chúng ta nên xác định một trang trí tff.tf_computation thay vì chỉ sử dụng một cơ chế hiện có như tf.function . Không giống như trong phần trước, ở đây chúng ta đang xử lý một khối mã TensorFlow thông thường.

Có một vài lý do giải thích cho điều này, cách xử lý đầy đủ vượt ra ngoài phạm vi của hướng dẫn này, nhưng bạn nên đặt tên cho nguyên nhân chính:

  • Để nhúng các khối xây dựng có thể tái sử dụng được triển khai bằng mã TensorFlow trong phần thân của các phép tính liên hợp, chúng cần phải đáp ứng các thuộc tính nhất định - chẳng hạn như được theo dõi và tuần tự hóa tại thời điểm xác định, có chữ ký kiểu, v.v. Điều này thường yêu cầu một số hình thức của trình trang trí.

Nói chung, chúng tôi khuyên bạn sử dụng cơ chế tự nhiên TensorFlow của cho các thành phần, chẳng hạn như tf.function , bất cứ nơi nào có thể, như cách chính xác trong đó tương tác trang trí TFF với chức năng háo hức có thể được dự kiến sẽ phát triển.

Bây giờ, trở lại với các mã ví dụ đoạn mã trên, việc tính toán add_half chúng tôi chỉ được xác định có thể được xử lý bằng TFF giống như bất kỳ tính toán TFF khác. Đặc biệt, nó có một chữ ký kiểu TFF.

str(add_half.type_signature)
'(float32 -> float32)'

Lưu ý rằng chữ ký loại này không có vị trí. Tính toán TensorFlow không thể sử dụng hoặc trả về các kiểu liên kết.

Bây giờ bạn cũng có thể sử dụng add_half như là một khối xây dựng trong tính toán khác. Ví dụ, dưới đây là cách bạn có thể sử dụng tff.federated_map điều hành để áp dụng add_half pointwise cho tất cả các thành phần thành viên của một phao liên trên các thiết bị của khách hàng.

@tff.federated_computation(tff.type_at_clients(tf.float32))
def add_half_on_clients(x):
  return tff.federated_map(add_half, x)
str(add_half_on_clients.type_signature)
'({float32}@CLIENTS -> {float32}@CLIENTS)'

Thực thi tính toán TensorFlow

Thi hành tính toán được xác định với tff.tf_computation theo quy tắc tương tự như những người chúng ta đã mô tả cho tff.federated_computation . Chúng có thể được gọi dưới dạng các lệnh gọi thông thường trong Python, như sau.

add_half_on_clients([1.0, 3.0, 2.0])
[<tf.Tensor: shape=(), dtype=float32, numpy=1.5>,
 <tf.Tensor: shape=(), dtype=float32, numpy=3.5>,
 <tf.Tensor: shape=(), dtype=float32, numpy=2.5>]

Một lần nữa, nó là đáng chú ý là cách gọi các tính toán add_half_on_clients theo cách này mô phỏng một quá trình phân phối. Dữ liệu được sử dụng trên máy khách và được trả lại trên máy khách. Thật vậy, tính toán này có mỗi máy khách thực hiện một hành động cục bộ. Không có tff.SERVER đề cập một cách rõ ràng trong hệ thống này (ngay cả khi trong thực tế, dàn xử lý như vậy có thể liên quan đến một). Hãy nghĩ về một tính toán được xác định theo cách này là khái niệm tương tự như Map sân khấu trong MapReduce .

Ngoài ra, hãy nhớ rằng những gì chúng tôi đã nói trong phần trước về tính toán TFF việc đăng lúc định nghĩa vẫn đúng đối với tff.tf_computation mã là tốt - cơ quan Python của add_half_on_clients được bắt nguồn từ một lần vào thời điểm xác định. Trong các lần gọi tiếp theo, TFF sử dụng biểu diễn tuần tự của nó.

Sự khác biệt duy nhất giữa phương pháp Python trang trí với tff.federated_computation và những trang trí bằng tff.tf_computation là sau này được đăng như đồ thị TensorFlow (trong khi cựu không được phép chứa mã TensorFlow trực tiếp nhúng trong họ).

Dưới mui xe, mỗi phương pháp trang trí bằng tff.tf_computation tạm thời vô hiệu hóa thực hiện háo hức để cho phép cấu trúc của tính toán để được chụp. Trong khi quá trình thực thi háo hức bị tắt cục bộ, bạn có thể sử dụng các cấu trúc TensorFlow, AutoGraph, TensorFlow 2.0 háo hức, v.v., miễn là bạn viết logic tính toán của mình theo cách sao cho nó có thể được tuần tự hóa một cách chính xác.

Ví dụ: mã sau sẽ không thành công:

try:

  # Eager mode
  constant_10 = tf.constant(10.)

  @tff.tf_computation(tf.float32)
  def add_ten(x):
    return x + constant_10

except Exception as err:
  print (err)
Attempting to capture an EagerTensor without building a function.

Ở trên không thành công vì constant_10 đã được xây dựng bên ngoài của đồ thị mà tff.tf_computation xây dựng nội bộ trong cơ thể của add_ten trong quá trình tuần tự.

Mặt khác, cách gọi chức năng python mà sửa đổi đồ thị hiện nay khi gọi bên trong một tff.tf_computation là tốt:

def get_constant_10():
  return tf.constant(10.)

@tff.tf_computation(tf.float32)
def add_ten(x):
  return x + get_constant_10()

add_ten(5.0)
15.0

Lưu ý rằng các cơ chế tuần tự hóa trong TensorFlow đang phát triển và chúng tôi mong đợi các chi tiết về cách TFF tuần tự hóa các phép tính cũng sẽ phát triển.

Làm việc với tf.data.Dataset s

Như đã đề cập trước đó, một tính năng độc đáo của tff.tf_computation s là họ cho phép bạn làm việc với tf.data.Dataset s định nghĩa trừu tượng như các thông số chính thức của mã của bạn. Các thông số được trình bày trong TensorFlow như bộ dữ liệu cần phải được khai báo sử dụng tff.SequenceType constructor.

Ví dụ, loại đặc điểm kỹ thuật tff.SequenceType(tf.float32) định nghĩa một chuỗi trừu tượng của các yếu tố float trong TFF. Chuỗi có thể chứa tensor hoặc cấu trúc lồng nhau phức tạp (chúng ta sẽ xem các ví dụ về chúng sau). Các đại diện súc tích của một chuỗi các T -typed mục là T* .

float32_sequence = tff.SequenceType(tf.float32)

str(float32_sequence)
'float32*'

Suppose that in our temperature sensor example, each sensor holds not just one temperature reading, but multiple. Here's how you can define a TFF computation in TensorFlow that calculates the average of temperatures in a single local data set using the tf.data.Dataset.reduce operator.

@tff.tf_computation(tff.SequenceType(tf.float32))
def get_local_temperature_average(local_temperatures):
  sum_and_count = (
      local_temperatures.reduce((0.0, 0), lambda x, y: (x[0] + y, x[1] + 1)))
  return sum_and_count[0] / tf.cast(sum_and_count[1], tf.float32)
str(get_local_temperature_average.type_signature)
'(float32* -> float32)'

In the body of a method decorated with tff.tf_computation , formal parameters of a TFF sequence type are represented simply as objects that behave like tf.data.Dataset , ie, support the same properties and methods (they are currently not implemented as subclasses of that type - this may change as the support for data sets in TensorFlow evolves).

You can easily verify this as follows.

@tff.tf_computation(tff.SequenceType(tf.int32))
def foo(x):
  return x.reduce(np.int32(0), lambda x, y: x + y)

foo([1, 2, 3])
6

Keep in mind that unlike ordinary tf.data.Dataset s, these dataset-like objects are placeholders. They don't contain any elements, since they represent abstract sequence-typed parameters, to be bound to concrete data when used in a concrete context. Support for abstractly-defined placeholder data sets is still somewhat limited at this point, and in the early days of TFF, you may encounter certain restrictions, but we won't need to worry about them in this tutorial (please refer to the documentation pages for details).

When locally executing a computation that accepts a sequence in a simulation mode, such as in this tutorial, you can feed the sequence as Python list, as below (as well as in other ways, eg, as a tf.data.Dataset in eager mode, but for now, we'll keep it simple).

get_local_temperature_average([68.5, 70.3, 69.8])
69.53333

Like all other TFF types, sequences like those defined above can use the tff.StructType constructor to define nested structures. For example, here's how one could declare a computation that accepts a sequence of pairs A , B , and returns the sum of their products. We include the tracing statements in the body of the computation so that you can see how the TFF type signature translates into the dataset's output_types and output_shapes .

@tff.tf_computation(tff.SequenceType(collections.OrderedDict([('A', tf.int32), ('B', tf.int32)])))
def foo(ds):
  print('element_structure = {}'.format(ds.element_spec))
  return ds.reduce(np.int32(0), lambda total, x: total + x['A'] * x['B'])
element_structure = OrderedDict([('A', TensorSpec(shape=(), dtype=tf.int32, name=None)), ('B', TensorSpec(shape=(), dtype=tf.int32, name=None))])
str(foo.type_signature)
'(<A=int32,B=int32>* -> int32)'
foo([{'A': 2, 'B': 3}, {'A': 4, 'B': 5}])
26

The support for using tf.data.Datasets as formal parameters is still somewhat limited and evolving, although functional in simple scenarios such as those used in this tutorial.

Putting it all together

Now, let's try again to use our TensorFlow computation in a federated setting. Suppose we have a group of sensors that each have a local sequence of temperature readings. We can compute the global temperature average by averaging the sensors' local averages as follows.

@tff.federated_computation(
    tff.type_at_clients(tff.SequenceType(tf.float32)))
def get_global_temperature_average(sensor_readings):
  return tff.federated_mean(
      tff.federated_map(get_local_temperature_average, sensor_readings))

Note that this isn't a simple average across all local temperature readings from all clients, as that would require weighing contributions from different clients by the number of readings they locally maintain. We leave it as an exercise for the reader to update the above code; the tff.federated_mean operator accepts the weight as an optional second argument (expected to be a federated float).

Also note that the input to get_global_temperature_average now becomes a federated float sequence . Federated sequences is how we will typically represent on-device data in federated learning, with sequence elements typically representing data batches (you will see examples of this shortly).

str(get_global_temperature_average.type_signature)
'({float32*}@CLIENTS -> float32@SERVER)'

Here's how we can locally execute the computation on a sample of data in Python. Notice that the way we supply the input is now as a list of list s. The outer list iterates over the devices in the group represented by tff.CLIENTS , and the inner ones iterate over elements in each device's local sequence.

get_global_temperature_average([[68.0, 70.0], [71.0], [68.0, 72.0, 70.0]])
70.0

This concludes the first part of the tutorial... we encourage you to continue on to the second part .