In [ ]:
epochs = 10
# We don't use the whole dataset for efficiency purpose, but feel free to increase these numbers
n_train_items = 640
n_test_items = 640
সার্ভিস সলিউশন (এমএলএএস/MLaaS) হিসাবে মেশিন লার্নিং তৈরি করার সময়, কোনও সংস্থাকে তার মডেলটি প্রশিক্ষণের জন্য অন্যান্য অংশীদারদের থেকে ডেটা অ্যাক্সেসের জন্য অনুরোধ করতে হবে। স্বাস্থ্য বা অর্থায়নে, মডেল এবং ডেটা উভয়ই অত্যন্ত সমালোচনামূলক: মডেল পরামিতিগুলি একটি ব্যবসায়িক সম্পদ হয় যখন ডেটা ব্যক্তিগত ডেটা থাকে যা শক্তভাবে নিয়ন্ত্রিত হয়।
এই প্রসঙ্গে, একটি সম্ভাব্য সমাধান হ'ল মডেল এবং ডেটা উভয়ই এনক্রিপ্ট করা এবং মেশিন লার্নিং মডেলকে এনক্রিপ্ট করা মানগুলির উপরে প্রশিক্ষণ দেওয়া। এটি গ্যারান্টি দেয় যে সংস্থা উদাহরণস্বরূপ রোগীদের চিকিত্সার রেকর্ডগুলিতে অ্যাক্সেস করবে না এবং যে স্বাস্থ্য সুবিধাগুলি তারা যে মডেলটির অবদান রাখবে তা পর্যবেক্ষণ করতে সক্ষম হবে না। বেশ কয়েকটি এনক্রিপশন স্কিম বিদ্যমান যা এনক্রিপ্ট হওয়া ডেটাগুলিতে গণনার অনুমতি দেয়, যার মধ্যে সিকিউর মাল্টি-পার্টির গণনা (এসএমপিসি/SMPC), হোমোমর্ফিক এনক্রিপশন (FHE/SHE) এবং ফাংশনাল এনক্রিপশন (FE) রয়েছে। আমরা এখানে মাল্টি পার্টি পার্টি কম্পিউটেশনে ফোকাস করব (যা টিউটোরিয়াল 5 তে প্রবর্তন করা হয়েছে) যা ব্যক্তিগত যোগমূলক ভাগ করে নেওয়ার সাথে সাথে ক্রিপ্টো প্রোটোকল সিকিউরএনএন এবং এসপিডিজেডের উপর নির্ভর করে।
এই টিউটোরিয়ালটির সঠিক সেটিংটি হ'ল: আপনি সার্ভার এবং আপনি model n $ শ্রমিকদের দ্বারা রাখা কিছু ডেটাতে আপনার মডেলটিকে প্রশিক্ষণ দিতে চান তা বিবেচনা করুন। সার্ভার সিক্রেট তার মডেল ভাগ করে এবং প্রতিটি ভাগ কর্মীর কাছে প্রেরণ করে। কর্মীরা গোপনীয়তার সাথে তাদের ডেটা ভাগ করে এবং তাদের মধ্যে এটি বিনিময় করে। আমরা যে কনফিগারেশনটি অধ্যয়ন করব সেগুলিতে 2 জন কর্মী আছেন: এলিস এবং বব। শেয়ার বিনিময় করার পরে, তাদের প্রত্যেকের এখন নিজস্ব একটি শেয়ার, অন্য শ্রমিকের একটি ভাগ এবং মডেলের একটি অংশ। গণনা এখন উপযুক্ত ক্রিপ্টো প্রোটোকল ব্যবহার করে মডেলটিকে ব্যক্তিগতভাবে প্রশিক্ষণ দেওয়া শুরু করতে পারে। মডেলটি প্রশিক্ষিত হয়ে গেলে, সমস্ত শেয়ার এটি ডিক্রিপ্ট করার জন্য সার্ভারে ফিরে পাঠানো যেতে পারে। এটি নিম্নলিখিত চিত্র সহ চিত্রিত:
এই প্রক্রিয়াটির উদাহরণ দেওয়ার জন্য, আসুন ধরে নেওয়া যাক এলিস এবং বব দুজনেই এমএনআইএসটি ডেটাসেটের একটি অংশ ধরে রাখি এবং আসুন ডিজিটের শ্রেণিবিন্যাস সম্পাদনের জন্য একটি মডেলকে প্রশিক্ষণ দিন!
লেখক:
অনুবাদক:
In [ ]:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
import time
এই শ্রেণিটি প্রশিক্ষণের জন্য সমস্ত হাইপার-প্যারামিটারগুলি বর্ণনা করে। মনে রাখবেন যে এগুলি এখানে সর্বজনীন।
In [ ]:
class Arguments():
def __init__(self):
self.batch_size = 64
self.test_batch_size = 64
self.epochs = epochs
self.lr = 0.02
self.seed = 1
self.log_interval = 1 # Log info at each batch
self.precision_fractional = 3
args = Arguments()
_ = torch.manual_seed(args.seed)
পাইসাইফ্ট আমদানি এখানে। আমরা দুজন রিমোট কর্মীর সাথে যোগাযোগ করি যা কল হয়alice
and bob
এবং আরেকটি কর্মীকে অনুরোধ করুন crypto_provider
আমাদের প্রয়োজন হতে পারে সমস্ত ক্রিপ্টো (crypto) প্রারম্ভিক(Primitive) কে দেয়।
In [ ]:
import syft as sy # import the Pysyft library
hook = sy.TorchHook(torch) # hook PyTorch to add extra functionalities like Federated and Encrypted Learning
# simulation functions
def connect_to_workers(n_workers):
return [
sy.VirtualWorker(hook, id=f"worker{i+1}")
for i in range(n_workers)
]
def connect_to_crypto_provider():
return sy.VirtualWorker(hook, id="crypto_provider")
workers = connect_to_workers(n_workers=2)
crypto_provider = connect_to_crypto_provider()
এখানে আমরা একটি ইউটিলিটি ফাংশন (utility function) ব্যবহার করছি যা নিম্নলিখিত আচরণের অনুকরণ করে: আমরা ধরে নিই যে এমএনআইএসটি (MNIST) ডেটাসেটটি আমাদের প্রতিটি শ্রমিকের হাতে থাকা প্রতিটি অংশে বিতরণ করা হয়েছে। শ্রমিকরা তখন তাদের ডেটাগুলি ব্যাচে(batches) বিভক্ত করে এবং গোপনে তাদের ডেটা একে অপরের মধ্যে ভাগ করে দেয়। ফিরে আসা চূড়ান্ত বস্তুটি এই গোপন ভাগ করা ব্যাচগুলির একটি পুনরাবৃত্তিযোগ্য, যা আমরা private data loader(ব্যক্তিগত ডেটা লোডার) বলি। নোট করুন যে প্রক্রিয়া চলাকালীন স্থানীয় কর্মী (তাই আমাদের/so us) ডেটা অ্যাক্সেস ছিল না।
আমরা যথারীতি একটি প্রশিক্ষণ এবং ব্যক্তিগত ডেটাসেট পরীক্ষা করি এবং ইনপুট এবং লেবেল উভয়ই গোপনে ভাগ করে নেওয়া হয়।
In [ ]:
def get_private_data_loaders(precision_fractional, workers, crypto_provider):
def one_hot_of(index_tensor):
"""
Transform to one hot tensor
Example:
[0, 3, 9]
=>
[[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]]
"""
onehot_tensor = torch.zeros(*index_tensor.shape, 10) # 10 classes for MNIST
onehot_tensor = onehot_tensor.scatter(1, index_tensor.view(-1, 1), 1)
return onehot_tensor
def secret_share(tensor):
"""
Transform to fixed precision and secret share a tensor
"""
return (
tensor
.fix_precision(precision_fractional=precision_fractional)
.share(*workers, crypto_provider=crypto_provider, requires_grad=True)
)
transformation = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
train_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=True, download=True, transform=transformation),
batch_size=args.batch_size
)
private_train_loader = [
(secret_share(data), secret_share(one_hot_of(target)))
for i, (data, target) in enumerate(train_loader)
if i < n_train_items / args.batch_size
]
test_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=False, download=True, transform=transformation),
batch_size=args.test_batch_size
)
private_test_loader = [
(secret_share(data), secret_share(target.float()))
for i, (data, target) in enumerate(test_loader)
if i < n_test_items / args.test_batch_size
]
return private_train_loader, private_test_loader
private_train_loader, private_test_loader = get_private_data_loaders(
precision_fractional=args.precision_fractional,
workers=workers,
crypto_provider=crypto_provider
)
এখানে আমরা যে মডেলটি ব্যবহার করব তা এটি একটি সহজ তবে এটি it has proved to perform reasonably well on MNIST
In [ ]:
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(28 * 28, 128)
self.fc2 = nn.Linear(128, 64)
self.fc3 = nn.Linear(64, 10)
def forward(self, x):
x = x.view(-1, 28 * 28)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
প্রশিক্ষণটি প্রায় যথারীতি করা হয়, আসল পার্থক্যটি হ'ল আমরা নেতিবাচক লগ-সম্ভাবনা negative log-likelihood (F.nll_loss in PyTorch) এর মতো ক্ষয়গুলি ব্যবহার করতে পারি না কারণ এসএমপিসি (SMPC) দিয়ে এই ফাংশনগুলি পুনরুত্পাদন করা বেশ জটিল। পরিবর্তে, আমরা একটি সরল গড়ের স্কোয়ার ত্রুটি হ্রাস(simpler Mean Square Error loss) ব্যবহার করি।
In [ ]:
def train(args, model, private_train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(private_train_loader): # <-- now it is a private dataset
start_time = time.time()
optimizer.zero_grad()
output = model(data)
# loss = F.nll_loss(output, target) <-- not possible here
batch_size = output.shape[0]
loss = ((output - target)**2).sum().refresh()/batch_size
loss.backward()
optimizer.step()
if batch_idx % args.log_interval == 0:
loss = loss.get().float_precision()
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}\tTime: {:.3f}s'.format(
epoch, batch_idx * args.batch_size, len(private_train_loader) * args.batch_size,
100. * batch_idx / len(private_train_loader), loss.item(), time.time() - start_time))
পরীক্ষার ফাংশন বদলায় না!
In [ ]:
def test(args, model, private_test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in private_test_loader:
start_time = time.time()
output = model(data)
pred = output.argmax(dim=1)
correct += pred.eq(target.view_as(pred)).sum()
correct = correct.get().float_precision()
print('\nTest set: Accuracy: {}/{} ({:.0f}%)\n'.format(
correct.item(), len(private_test_loader)* args.test_batch_size,
100. * correct.item() / (len(private_test_loader) * args.test_batch_size)))
এখানে কী ঘটছে সে সম্পর্কে কয়েকটি নোট। প্রথমত, আমরা গোপনে আমাদের কর্মীদের জুড়ে সমস্ত মডেল পরামিতি ভাগ করি। দ্বিতীয়ত, আমরা অপ্টিমাইজারের হাইপারপ্যারামিটারগুলি (optimizer's hyperparameters) স্থির নির্ভুলতায় রূপান্তর করি। নোট করুন যে আমাদের সেগুলি গোপনীয়ভাবে ভাগ করে নেওয়ার দরকার নেই কারণ এগুলি আমাদের প্রসঙ্গে প্রকাশ্য, তবে গোপনীয়ভাবে ভাগ করা মূল্যবোধগুলি সীমাবদ্ধ ক্ষেত্রগুলিতে বাস করে বলে আমাদের ধারাবাহিকভাবে ক্রিয়াকলাপ সম্পাদন করার জন্য তাদের এখনও ite .fix_precision
ব্যবহার করে সীমাবদ্ধ ক্ষেত্রগুলিতে সরানো দরকার ওজন আপডেট $W \leftarrow W - \alpha * \Delta W$.
In [ ]:
model = Net()
model = model.fix_precision().share(*workers, crypto_provider=crypto_provider, requires_grad=True)
optimizer = optim.SGD(model.parameters(), lr=args.lr)
optimizer = optimizer.fix_precision()
for epoch in range(1, args.epochs + 1):
train(args, model, private_train_loader, optimizer, epoch)
test(args, model, private_test_loader)
তুমি এখানে! আপনি মাত্র 100% এনক্রিপ্টড প্রশিক্ষণ ব্যবহার করে এমএনআইএসটি ডেটাসেটের একটি ক্ষুদ্র ভগ্নাংশ ব্যবহার করে 75% নির্ভুলতা পাবেন!
প্রথম জিনিসটি স্পষ্টতই চলমান সময়! আপনি অবশ্যই লক্ষ্য করেছেন, এটি সাধারণ পাঠ্য প্রশিক্ষণের চেয়ে ধীর। বিশেষত, 64 টি আইটেমের 1 ব্যাচের ওপরে পুনরাবৃত্তিটি 3.2 সেকেন্ড লাগে যখন খাঁটি পাইটর্চে কেবল 13 মিমি থাকে। যদিও এটি কোনও ব্লকারের মতো মনে হতে পারে, কেবল মনে রাখবেন যে এখানে সবকিছু দূর থেকে এবং এনক্রিপ্ট করা বিশ্বে ঘটেছিল: কোনও একক ডেটা আইটেম প্রকাশ করা হয়নি। আরও সুনির্দিষ্টভাবে বলা যায় যে, একটি আইটেমটি প্রক্রিয়া করার সময় 50ms যা খুব খারাপ নয়। আসল প্রশ্নটি হ'ল এনক্রিপ্ট করা প্রশিক্ষণের প্রয়োজন হয় এবং যখন কেবল এনক্রিপ্ট করা পূর্বাভাসই যথেষ্ট analy পূর্বাভাসটি সম্পাদনের 50ms কোনও উত্পাদন-প্রস্তুত দৃশ্যে সম্পূর্ণ গ্রহণযোগ্য!
একটি প্রধান বাধা ব্যয়বহুল অ্যাক্টিভেশন ফাংশন ব্যবহার: এসএমপিসির (SMPC) সাথে রিলু (ReLU) অ্যাক্টিভেশন খুব ব্যয়বহুল কারণ এটি ব্যক্তিগত তুলনা এবং সিকিউরএনএন (SecureNN) প্রোটোকল ব্যবহার করে। উদাহরণস্বরূপ, আমরা যদি ক্রিট্টোনেটসের (CryptoNets) মতো এনক্রিপ্ট করা গণনার ক্ষেত্রে বেশ কয়েকটি গবেষণাপত্রে করা হয় যেমন আমরা চতুষ্কোণীয় অ্যাক্টিভেশন (quadratic activation) দিয়ে রিলুকে প্রতিস্থাপন করি তবে আমরা 3.2 থেকে 1.2 তে নেমেছি।
একটি সাধারণ নিয়ম হিসাবে, মূল ধারণাটি হ'ল প্রয়োজনীয় জিনিসগুলি কেবল এনক্রিপ্ট করা এবং এই টিউটোরিয়ালটি আপনাকে বোঝায় যে এটি কতটা সহজ হতে পারে।
আপনি বিস্মিত হতে পারেন যে আমরা সীমাবদ্ধ ক্ষেত্রগুলিতে পূর্ণসংখ্যার সাথে কাজ করলেও আমরা কীভাবে ব্যাকপ্রসারণ এবং গ্রেডিয়েন্ট আপডেটগুলি (backpropagation and gradient updates) করব। এটি করার জন্য, আমরা অটোগ্রাডটেনসর (AutogradTensor) নামে একটি নতুন সিফ্ট টেনসর তৈরি করেছি। আপনি যদি না ও দেখে থাকেন তবে এই টিউটোরিয়ালটি এটি নিবিড়ভাবে ব্যবহার করেছে! কোনও মডেলের ওজন মুদ্রণ (printing) করে এটি পরীক্ষা করা যাক:
In [ ]:
model.fc3.bias
এবং একটি তথ্য আইটেম (And a data item)
In [ ]:
first_batch, input_data = 0, 0
private_train_loader[first_batch][input_data]
আপনি পর্যবেক্ষণ করতে পারছেন অটোগ্রাডটেন্সার (AutogradTensor) আছে! এটি torch আবরণ এবং ফিক্সডপ্রেসিশনটেনসরের (FixedPrecisionTensor) মধ্যে বাস করে যা নির্দেশ করে যে মানগুলি এখন সীমাবদ্ধ ক্ষেত্রগুলিতে রয়েছে। এই অটোগ্র্যাডটেন্সারের (AutogradTensor) লক্ষ্য হ'ল যখন এনক্রিপ্ট করা মানগুলিতে অপারেশন করা হয় তখন গণনা গ্রাফ সংরক্ষণ করা। এটি কার্যকর কারণ কারণ ব্যাকপ্রোগেশনের (backpropagation) জন্য পিছনে কল করার সময়, এই অটোগ্রাডটেন্সার সমস্ত পশ্চাদপট ফাংশনগুলি ওভাররাইড (overrides) করে যা এনক্রিপ্ট করা গণনার সাথে সামঞ্জস্যপূর্ণ নয় এবং এই গ্রেডিয়েন্টগুলি কীভাবে গণনা করতে হবে তা নির্দেশ করে। উদাহরণস্বরূপ, গুণটি সম্পর্কে যা বিভার ট্রিপলস ট্রিক (Beaver triples trick) ব্যবহার করে করা হয়, আমরা সেই কৌশলটি আরও বেশি আলাদা করতে চাই না যে কোনও গুণকে আলাদা করা খুব সহজ হওয়া উচিত: $\partial_b (a \cdot b) = a \cdot \partial b$. এই গ্রেডিয়েন্টগুলি (gradients) কীভাবে গণনা করা যায় তা এখানে আমরা বর্ণনা করব:
class MulBackward(GradFunc):
def __init__(self, self_, other):
super().__init__(self, self_, other)
self.self_ = self_
self.other = other
def gradient(self, grad):
grad_self_ = grad * self.other
grad_other = grad * self.self_ if type(self.self_) == type(self.other) else None
return (grad_self_, grad_other)
আপনি একবার তাকান করতে পারেন tensors/interpreters/gradients.py
আমরা কীভাবে আরও গ্রেডিয়েন্ট প্রয়োগ করেছি তা যদি জানতে আগ্রহী হন।
গণনা গ্রাফের ক্ষেত্রে, এর অর্থ গ্রাফের একটি অনুলিপি স্থানীয় রয়ে গেছে এবং যে সার্ভারটি ফরোয়ার্ড পাসের সাথে সমন্বয় সাধন করে তা পিছিয়ে পাস কীভাবে করতে হয় তার নির্দেশাবলীও সরবরাহ করে। এটি আমাদের সেটিংয়ে একটি সম্পূর্ণ বৈধ অনুমান।
সর্বশেষে, আসুন আমরা এখানে যে সুরক্ষা পাচ্ছি সে সম্পর্কে কয়েকটি ইঙ্গিত দেওয়া যাক: আমরা এখানে যে বিরোধীরা বিবেচনা করছি তারা হলেন সৎ কিন্তু কৌতূহলী (honest but curious): এর অর্থ হল যে কোনও শত্রুরা এই প্রোটোকলটি চালিয়ে ডেটা সম্পর্কে কিছুই জানতে পারে না, তবে একটি দূষিত শত্রু এখনও প্রোটোকল থেকে বিচ্যুত হতে পারে এবং উদাহরণস্বরূপ গণনাটি নাশকতার জন্য শেয়ারকে দূষিত করার চেষ্টা করে। বেসরকারী তুলনা সহ এসএমপিসি (SMPC) গণনাগুলিতে দূষিত বিরোধীদের বিরুদ্ধে সুরক্ষা এখনও একটি উন্মুক্ত সমস্যা।
তদতিরিক্ত, সিকিওর মাল্টি-পার্টির (Secure Multi-Party) গণনাও যদি নিশ্চিত করে যে প্রশিক্ষণের ডেটা অ্যাক্সেস করা হয়নি তবে প্লেইন টেক্সট ওয়ার্ল্ড থেকে অনেক হুমকি এখনও এখানে উপস্থিত রয়েছে। উদাহরণস্বরূপ, আপনি যেমন মডেলটির কাছে অনুরোধ করতে পারেন (MLaaS প্রসঙ্গে), আপনি ভবিষ্যদ্বাণীগুলি পেতে পারেন যা প্রশিক্ষণ ডেটাসেট সম্পর্কে তথ্য প্রকাশ করতে পারে। বিশেষত সদস্যপদ আক্রমণগুলির বিরুদ্ধে আপনার কোনও সুরক্ষা নেই, মেশিন লার্নিং পরিষেবাগুলিতে একটি সাধারণ আক্রমণ যেখানে বিরোধীরা নির্ধারণ করতে চায় যে কোনও নির্দিষ্ট আইটেমটি ডেটাসেটে ব্যবহৃত হয়েছিল কিনা। এগুলি ছাড়াও অন্যান্য আক্রমণ যেমন অনিচ্ছাকৃত মুখস্থকরণ প্রক্রিয়াগুলি (কোনও ডেটা আইটেমের সুনির্দিষ্ট বৈশিষ্ট্য শেখা মডেল), মডেল বিপরীতকরণ বা নিষ্কাশন এখনও সম্ভব।
উপরে উল্লিখিত হুমকির অনেকের জন্য কার্যকর একটি সাধারণ সমাধান হ'ল ডিফারেনশিয়াল প্রাইভেসি যুক্ত করা। এটি সিকিউর মাল্টি-পার্টির গণনার সাথে সুন্দরভাবে মিলিত হতে পারে এবং খুব আকর্ষণীয় সুরক্ষা গ্যারান্টি সরবরাহ করতে পারে। আমরা বর্তমানে বেশ কয়েকটি বাস্তবায়নের উপর কাজ করছি এবং আশা করি একটি উদাহরণ প্রস্তাব করুন যা উভয়ই সংক্ষেপে সংযুক্ত করা হবে!
যেমনটি আপনি দেখেছেন, এসএমপিসি (SMPC) ব্যবহার করে কোনও মডেলকে প্রশিক্ষণ দেওয়া কোডের দৃষ্টিকোণ থেকে জটিল নয়, এমনকি আমরা হুডের (hood) নিচে জটিল বস্তুগুলিও ব্যবহার করি। এটি মাথায় রেখে, আপনার প্রশিক্ষণের জন্য বা মূল্যায়নের জন্য কখন এনক্রিপ্ট করা গণনা প্রয়োজন তা দেখার জন্য আপনার এখন ব্যবহারের ক্ষেত্রে বিশ্লেষণ করা উচিত। যদি এনক্রিপ্ট করা গণনা সাধারণভাবে খুব ধীর হয় তবে এটি সাবধানতার সাথে ব্যবহার করা যেতে পারে যাতে সামগ্রিক গণনা ওভারহেড হ্রাস পায়।
আপনি যদি এটি উপভোগ করেন এবং গোপনীয়তা সংরক্ষণ, AI এবং AI সরবরাহ চেইনের (ডেটা) বিকেন্দ্রীভূত মালিকানার দিকে আন্দোলনে যোগ দিতে চান, আপনি নিম্নলিখিত উপায়ে এটি করতে পারেন!
আমাদের সম্প্রদায়কে সাহায্য করার সবচেয়ে সহজ উপায় হ'ল রিপোসিটোরি গুলোতে ষ্টার করা এটি আমরা যে অসাধারণ সরঞ্জামগুলি তৈরি করছি তার সচেতনতা বাড়াতে সহায়তা করে।
ফেডারেটেড এবং প্রাইভেসি-প্রিজারভেভিং লার্নিংয়ের ( Federated and Privacy-Preserving Learning) দৃশ্য কেমন হওয়া উচিত এবং আমরা এটির জন্য bricks কীভাবে তৈরি করছি সে সম্পর্কে আরও ভাল ধারণা পেতে আমরা সত্যিই দুর্দান্ত টিউটোরিয়াল তৈরি করেছি।
সর্বশেষতম অগ্রগতিতে আপ টু ডেট রাখার সর্বোত্তম উপায় হ'ল আমাদের সম্প্রদায়ে যোগদান করা!
আমাদের সম্প্রদায়ে অবদান রাখার সর্বোত্তম উপায় হ'ল কোড অবদানকারী হয়ে উঠুন! আপনি যদি মিনি-প্রকল্পগুলি "ওয়ান অফ" শুরু করতে চান তবে আপনি পাইসাইফ্ট গিটহাব ইস্যু পৃষ্ঠাতে গিয়ে চিহ্নিত বিষয়গুলির জন্য অনুসন্ধান করতে পারেন Good First Issue
.
আপনার যদি আমাদের কোডবেসে অবদান রাখার সময় না থাকে তবে তবুও সমর্থন leণ দিতে চান, আপনি আমাদের ওপেন কালেক্টিভেরও ব্যাকের হয়ে উঠতে পারেন। সমস্ত অনুদান আমাদের ওয়েব হোস্টিং এবং অন্যান্য সম্প্রদায় ব্যয় যেমন হ্যাকাথনস এবং মেটআপগুলির দিকে যায়!
In [ ]:
In [ ]: