Last chance! 50% off DataCamp Premium
Sale ends in
रीकरंट न्यूरल नेटवर्क (RNN) एक प्रकार का कृत्रिम न्यूरल नेटवर्क (ANN) है, जिसका उपयोग Apple के Siri और Google के वॉइस सर्च में किया जाता है। RNN में एक आंतरिक मेमोरी होती है जो पिछले इनपुट्स को याद रखती है, जो स्टॉक की कीमतों का पूर्वानुमान, टेक्स्ट जनरेशन, ट्रांसक्रिप्शन और मशीन अनुवाद के लिए उपयोगी है।
पारंपरिक न्यूरल नेटवर्क में इनपुट और आउटपुट एक-दूसरे से स्वतंत्र होते हैं, जबकि RNN में आउटपुट अनुक्रम के पूर्ववर्ती तत्वों पर निर्भर करता है। रीकरंट नेटवर्क नेटवर्क की प्रत्येक लेयर में पैरामीटर्स भी साझा करते हैं। फीडफॉर्वर्ड नेटवर्क में प्रत्येक नोड के लिए अलग-अलग वेट होते हैं। जबकि RNN में नेटवर्क की प्रत्येक लेयर के भीतर समान वेट साझा किए जाते हैं और ग्रेडिएंट डिसेंट के दौरान, लॉस कम करने के लिए वेट और बायस को अलग-अलग समायोजित किया जाता है।

ऊपर दिया गया चित्र रीकरंट न्यूरल नेटवर्क्स का एक सरल निरूपण है। यदि हम सरल डेटा [45,56,45,49,50,…] का उपयोग करके स्टॉक की कीमतों का पूर्वानुमान कर रहे हैं, तो X0 से Xt तक प्रत्येक इनपुट में एक पिछला मान होगा। उदाहरण के लिए, X0 में 45 होगा, X1 में 56 होगा, और इन मानों का उपयोग अनुक्रम में अगली संख्या का पूर्वानुमान लगाने के लिए किया जाता है।
RNN में जानकारी एक लूप के माध्यम से संचरित होती रहती है, इसलिए आउटपुट वर्तमान इनपुट और पहले प्राप्त इनपुट्स दोनों से निर्धारित होता है।

इनपुट लेयर X प्रारंभिक इनपुट को प्रोसेस करती है और उसे मध्य लेयर A तक पहुंचाती है। मध्य लेयर में कई हिडन लेयर्स होती हैं, जिनमें से प्रत्येक के अपने एक्टिवेशन फंक्शंस, वेट्स और बायस होते हैं। इन पैरामीटर्स को पूरे हिडन लेयर में मानकीकृत किया जाता है ताकि कई हिडन लेयर्स बनाने के बजाय एक ही लेयर बनाई जाए और उसे लूप कराया जाए।
पारंपरिक बैकप्रोपेगेशन के बजाय, रीकरंट न्यूरल नेटवर्क्स ग्रेडिएंट निकालने के लिए बैकप्रोपेगेशन थ्रू टाइम (BPTT) एल्गोरिदम का उपयोग करते हैं। बैकप्रोपेगेशन में, मॉडल आउटपुट से इनपुट लेयर तक त्रुटियों की गणना करके पैरामीटर समायोजित करता है। BPTT प्रत्येक टाइम स्टेप पर त्रुटि को जोड़ता है क्योंकि RNN प्रत्येक लेयर में पैरामीटर्स साझा करता है। RNNs और उनके कार्य-प्रणाली के बारे में अधिक जानें: What are Recurrent Neural Networks?.
फीडफॉर्वर्ड नेटवर्क्स में एकल इनपुट और आउटपुट होता है, जबकि रीकरंट न्यूरल नेटवर्क्स लचीले होते हैं क्योंकि इनपुट और आउटपुट की लंबाई बदली जा सकती है। यह लचीलापन RNNs को संगीत जनरेशन, सेंटिमेंट वर्गीकरण और मशीन अनुवाद जैसे कार्यों में सक्षम बनाता है।
इनपुट और आउटपुट की अलग-अलग लंबाई के आधार पर RNN के चार प्रकार होते हैं।

कन्वोल्यूशनल न्यूरल नेटवर्क (CNN) एक फीड-फॉरवर्ड न्यूरल नेटवर्क है जो स्थानिक डेटा को प्रोसेस करने में सक्षम होता है। इसे आमतौर पर कंप्यूटर विज़न एप्लिकेशंस जैसे इमेज क्लासिफिकेशन के लिए इस्तेमाल किया जाता है। साधारण न्यूरल नेटवर्क सरल बाइनरी वर्गीकरण में अच्छे होते हैं, लेकिन वे पिक्सल निर्भरताओं वाली छवियों को संभाल नहीं पाते। CNN मॉडल आर्किटेक्चर में convolutional लेयर्स, ReLU लेयर्स, pooling लेयर्स और पूर्णतः कनेक्टेड आउटपुट लेयर्स शामिल होते हैं। आप इस प्रोजेक्ट पर काम करके CNN सीख सकते हैं: Python में Convolutional Neural Networks।

सरल RNN मॉडल आमतौर पर दो प्रमुख समस्याओं का सामना करते हैं। ये समस्याएँ ग्रेडिएंट से संबंधित होती हैं, जो लॉस फ़ंक्शन का ढाल (slope) है और एरर फ़ंक्शन के साथ बदलता है।
इन समस्याओं का सरल समाधान न्यूरल नेटवर्क के भीतर हिडन लेयर्स की संख्या कम करना है, जिससे RNN की कुछ जटिलता घटेगी। इन समस्याओं का समाधान उन्नत RNN आर्किटेक्चर जैसे LSTM और GRU का उपयोग करके भी किया जा सकता है।
सरल RNN के दोहराए जाने वाले मॉड्यूल में एकल tanh लेयर के साथ बुनियादी संरचना होती है। RNN की यह सरल संरचना शॉर्ट मेमोरी से जूझती है, जहाँ यह बड़े अनुक्रमिक डेटा में पिछले टाइम स्टेप्स की जानकारी बनाए रखने में संघर्ष करती है। इन समस्याओं को Long Short Term Memory (LSTM) और Gated Recurrent Unit (GRU) आसानी से हल कर सकते हैं, क्योंकि वे लंबे समय तक जानकारी याद रखने में सक्षम होते हैं।

Long Short Term Memory (LSTM) RNN का उन्नत प्रकार है, जिसे vanishing और exploding दोनों ग्रेडिएंट समस्याओं को रोकने के लिए डिज़ाइन किया गया था। RNN की तरह, LSTM में भी दोहराए जाने वाले मॉड्यूल होते हैं, लेकिन इसकी संरचना अलग होती है। एकल tanh लेयर के बजाय, LSTM में चार परस्पर क्रियाशील लेयर्स होती हैं जो एक-दूसरे से संचार करती हैं। यह चार-लेयरी संरचना LSTM को लंबी अवधि की मेमोरी बनाए रखने में मदद करती है और इसे कई अनुक्रमिक समस्याओं में उपयोग किया जा सकता है, जिनमें मशीन अनुवाद, स्पीच सिंथेसिस, स्पीच रिकग्निशन और हैंडराइटिंग रिकग्निशन शामिल हैं। आप इस गाइड का अनुसरण करके LSTM में हाथ-on अनुभव प्राप्त कर सकते हैं: स्टॉक प्रेडिक्शंस के लिए Python LSTM।

Gated Recurrent Unit (GRU) LSTM का एक रूपांतर है क्योंकि दोनों में डिज़ाइन समानताएँ हैं, और कुछ मामलों में वे समान परिणाम देते हैं। GRU vanishing gradient समस्या को हल करने के लिए update gate और reset gate का उपयोग करता है। ये गेट यह तय करते हैं कि कौन-सी जानकारी महत्वपूर्ण है और उसे आउटपुट तक पहुँचाते हैं। गेट्स को बहुत पहले की जानकारी संग्रहीत करने के लिए प्रशिक्षित किया जा सकता है, ताकि समय के साथ वह धुंधली न पड़े या अप्रासंगिक जानकारी हटाई जा सके।
LSTM के विपरीत, GRU में सेल स्टेट Ct नहीं होता। इसमें केवल हिडन स्टेट ht होता है, और सरल आर्किटेक्चर के कारण GRU का ट्रेनिंग समय LSTM मॉडलों की तुलना में कम होता है। GRU आर्किटेक्चर समझने में आसान है क्योंकि यह इनपुट xt और पिछले टाइमस्टैम्प का हिडन स्टेट ht-1 लेता है और नया हिडन स्टेट ht आउटपुट देता है। आप यहाँ GRU के बारे में गहन जानकारी प्राप्त कर सकते हैं: Understanding GRU Networks।

इस प्रोजेक्ट में, हम Kaggle का MasterCard स्टॉक डेटासेट 25-मई-2006 से 11-अक्टूबर-2021 तक उपयोग करेंगे और स्टॉक कीमत का पूर्वानुमान लगाने के लिए LSTM और GRU मॉडल ट्रेन करेंगे। यह एक सरल प्रोजेक्ट-आधारित ट्यूटोरियल है, जहाँ हम डेटा का विश्लेषण करेंगे, एडवांस्ड RNN मॉडलों पर ट्रेन करने हेतु डेटा का प्रीप्रोसेसिंग करेंगे, और अंत में परिणामों का मूल्यांकन करेंगे।
प्रोजेक्ट के लिए डेटा मैनिपुलेशन हेतु Pandas और Numpy, डेटा विज़ुअलाइज़ेशन हेतु Matplotlib.pyplot, स्केलिंग और मूल्यांकन हेतु scikit-learn, और मॉडलिंग हेतु TensorFlow की आवश्यकता होगी। हम पुनरुत्पादकता के लिए सीड्स भी सेट करेंगे।
# Importing the libraries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, LSTM, Dropout, GRU, Bidirectional
from tensorflow.keras.optimizers import SGD
from tensorflow.random import set_seed
set_seed(455)
np.random.seed(455)
इस भाग में, हम Date कॉलम को इंडेक्स में जोड़कर और उसे DateTime फ़ॉर्मेट में बदलकर MasterCard डेटासेट इम्पोर्ट करेंगे। हम डेटासेट से अप्रासंगिक कॉलम भी हटाएँगे क्योंकि हमें केवल स्टॉक कीमतें, वॉल्यूम और तिथि में रुचि है।
डेटासेट में इंडेक्स के रूप में Date और कॉलम के रूप में Open, High, Low, Close और Volume हैं। लगता है कि हमने सफलतापूर्वक एक साफ-सुथरा डेटासेट इम्पोर्ट कर लिया है।
dataset = pd.read_csv(
"data/Mastercard_stock_history.csv", index_col="Date", parse_dates=["Date"]
).drop(["Dividends", "Stock Splits"], axis=1)
print(dataset.head())
Open High Low Close Volume
Date
2006-05-25 3.748967 4.283869 3.739664 4.279217 395343000
2006-05-26 4.307126 4.348058 4.103398 4.179680 103044000
2006-05-30 4.183400 4.184330 3.986184 4.093164 49898000
2006-05-31 4.125723 4.219679 4.125723 4.180608 30002000
2006-06-01 4.179678 4.474572 4.176887 4.419686 62344000
.describe() फ़ंक्शन हमें डेटा का गहराई से विश्लेषण करने में मदद करता है। आइए High कॉलम पर ध्यान दें क्योंकि हम इसका उपयोग मॉडल को ट्रेन करने के लिए करने वाले हैं। हम मॉडल फीचर के रूप में Close या Open कॉलम भी चुन सकते हैं, लेकिन High अधिक अर्थपूर्ण है क्योंकि यह बताता है कि दिए गए दिन में शेयर का उच्चतम मान कितना रहा।
न्यूनतम स्टॉक कीमत $4.10 है, और अधिकतम $400.5 है। औसत $105.9 और मानक विचलन $107.3 है, जिसका अर्थ है कि स्टॉक्स में उच्च वैरिएंस है।
print(dataset.describe())
Open High Low Close Volume
count 3872.000000 3872.000000 3872.000000 3872.000000 3.872000e+03
mean 104.896814 105.956054 103.769349 104.882714 1.232250e+07
std 106.245511 107.303589 105.050064 106.168693 1.759665e+07
min 3.748967 4.102467 3.739664 4.083861 6.411000e+05
25% 22.347203 22.637997 22.034458 22.300391 3.529475e+06
50% 70.810079 71.375896 70.224002 70.856083 5.891750e+06
75% 147.688448 148.645373 146.822013 147.688438 1.319775e+07
max 392.653890 400.521479 389.747812 394.685730 3.953430e+08
.isna().sum() का उपयोग करके हम डेटासेट में मिसिंग वैल्यूज़ का पता लगा सकते हैं। लगता है कि डेटासेट में कोई मिसिंग वैल्यू नहीं है।
dataset.isna().sum()
Open 0
High 0
Low 0
Close 0
Volume 0
dtype: int64
train_test_plot फ़ंक्शन तीन आर्ग्युमेंट्स लेता है: dataset, tstart, और tend तथा एक साधारण रेखा-चित्र (लाइन ग्राफ) बनाता है। tstart और tend वर्षों में समय सीमाएँ हैं। हम विशिष्ट अवधियों का विश्लेषण करने के लिए इन आर्ग्युमेंट्स को बदल सकते हैं। लाइन प्लॉट को दो भागों में विभाजित किया गया है: ट्रेन और टेस्ट। इससे हमें टेस्ट डेटासेट के वितरण का निर्णय लेने में मदद मिलेगी।
MasterCard के स्टॉक मूल्य 2016 से बढ़ते रहे हैं। 2020 की पहली तिमाही में इसमें गिरावट आई, लेकिन वर्ष के उत्तरार्ध में यह स्थिर स्थिति में आ गया। हमारा टेस्ट डेटासेट एक वर्ष का है, 2021 से 2022 तक, और शेष डेटासेट ट्रेनिंग के लिए उपयोग किया गया है।
tstart = 2016
tend = 2020
def train_test_plot(dataset, tstart, tend):
dataset.loc[f"{tstart}":f"{tend}", "High"].plot(figsize=(16, 4), legend=True)
dataset.loc[f"{tend+1}":, "High"].plot(figsize=(16, 4), legend=True)
plt.legend([f"Train (Before {tend+1})", f"Test ({tend+1} and beyond)"])
plt.title("MasterCard stock price")
plt.show()
train_test_plot(dataset,tstart,tend)

train_test_split फ़ंक्शन डेटासेट को दो सबसेट्स में विभाजित करता है: training_set और test_set।
def train_test_split(dataset, tstart, tend):
train = dataset.loc[f"{tstart}":f"{tend}", "High"].values
test = dataset.loc[f"{tend+1}":, "High"].values
return train, test
training_set, test_set = train_test_split(dataset, tstart, tend)
हम अपने प्रशिक्षण सेट को मानकीकृत करने के लिए MinMaxScaler फ़ंक्शन का उपयोग करेंगे, जो हमें बाह्य मान या विसंगतियों से बचने में मदद करेगा। आप अपने डेटा को सामान्यीकृत करने और मॉडल प्रदर्शन में सुधार के लिए StandardScaler या कोई अन्य स्केलर फ़ंक्शन भी आज़मा सकते हैं।
sc = MinMaxScaler(feature_range=(0, 1))
training_set = training_set.reshape(-1, 1)
training_set_scaled = sc.fit_transform(training_set)
split_sequence फ़ंक्शन प्रशिक्षण डेटासेट का उपयोग करता है और उसे इनपुट्स (X_train) और आउटपुट्स (y_train) में बदलता है।
उदाहरण के लिए, यदि अनुक्रम [1,2,3,4,5,6,7,8,9,10,11,12] है और n_step तीन है, तो यह अनुक्रम को तीन इनपुट टाइमस्टैम्प्स और एक आउटपुट में नीचे दिखाए अनुसार बदल देगा:
| X | y |
|---|---|
| 1,2,3 | 4 |
| 2,3,4 | 5 |
| 3,4,5 | 6 |
| 4,5,6 | 7 |
| … | … |
इस प्रोजेक्ट में, हम 60 n_steps का उपयोग कर रहे हैं। हम मॉडल प्रदर्शन को अनुकूलित करने के लिए स्टेप्स की संख्या घटा या बढ़ा भी सकते हैं।
def split_sequence(sequence, n_steps):
X, y = list(), list()
for i in range(len(sequence)):
end_ix = i + n_steps
if end_ix > len(sequence) - 1:
break
seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
X.append(seq_x)
y.append(seq_y)
return np.array(X), np.array(y)
n_steps = 60
features = 1
# split into samples
X_train, y_train = split_sequence(training_set_scaled, n_steps)
हम एक-वेरिएट सीरीज़ के साथ काम कर रहे हैं, इसलिए फीचर्स की संख्या एक है, और हमें X_train को LSTM मॉडल में फिट करने के लिए रीशेप करना होगा। X_train [samples, timesteps] के रूप में है, और हम इसे [samples, timesteps, features] में रीशेप करेंगे।
# Reshaping X_train for model
X_train = X_train.reshape(X_train.shape[0],X_train.shape[1],features)
मॉडल में LSTM की एक एकल हिडन लेयर और एक आउटपुट लेयर शामिल है। आप यूनिट्स की संख्या के साथ प्रयोग कर सकते हैं; अधिक यूनिट्स आमतौर पर बेहतर परिणाम देंगी। इस प्रयोग के लिए, हम LSTM यूनिट्स 125, सक्रियण के लिए tanh, और इनपुट आकार सेट करेंगे।
लेखक का नोट: TensorFlow लाइब्रेरी उपयोगकर्ता-अनुकूल है, इसलिए हमें LSTM या GRU मॉडल शून्य से बनाने की आवश्यकता नहीं है। हम बस LSTM या GRU मॉड्यूल्स का उपयोग करके मॉडल बनाएँगे।
अंत में, हम मॉडल को RMSprop ऑप्टिमाइज़र और लॉस फ़ंक्शन के रूप में mean square error के साथ कम्पाइल करेंगे।
# The LSTM architecture
model_lstm = Sequential()
model_lstm.add(LSTM(units=125, activation="tanh", input_shape=(n_steps, features)))
model_lstm.add(Dense(units=1))
# Compiling the model
model_lstm.compile(optimizer="RMSprop", loss="mse")
model_lstm.summary()
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm (LSTM) (None, 125) 63500
_________________________________________________________________
dense (Dense) (None, 1) 126
=================================================================
Total params: 63,626
Trainable params: 63,626
Non-trainable params: 0
_________________________________________________________________
मॉडल 50 epochs पर 32 बैच साइज के साथ ट्रेन होगा। आप प्रशिक्षण समय कम करने या परिणाम सुधारने के लिए हाइपरपैरामीटर्स बदल सकते हैं। मॉडल ट्रेनिंग सर्वश्रेष्ठ संभव लॉस के साथ सफलतापूर्वक पूरी हुई।
model_lstm.fit(X_train, y_train, epochs=50, batch_size=32)
Epoch 50/50
38/38 [==============================] - 1s 30ms/step - loss: 3.1642e-04
हम प्रीप्रोसेसिंग को दोहराएँगे और टेस्ट सेट को नॉर्मलाइज़ करेंगे। सबसे पहले हम ट्रांसफॉर्म करेंगे, फिर डेटासेट को सैंपल्स में बाँटेंगे, उसे रीशेप करेंगे, प्रेडिक्ट करेंगे, और प्रेडिक्शंस को इनवर्स ट्रांसफॉर्म करके मानक रूप में लाएँगे।
dataset_total = dataset.loc[:,"High"]
inputs = dataset_total[len(dataset_total) - len(test_set) - n_steps :].values
inputs = inputs.reshape(-1, 1)
#scaling
inputs = sc.transform(inputs)
# Split into samples
X_test, y_test = split_sequence(inputs, n_steps)
# reshape
X_test = X_test.reshape(X_test.shape[0], X_test.shape[1], features)
#prediction
predicted_stock_price = model_lstm.predict(X_test)
#inverse transform the values
predicted_stock_price = sc.inverse_transform(predicted_stock_price)
plot_predictions फ़ंक्शन वास्तविक बनाम अनुमानित रेखा-चित्र प्लॉट करेगा। इससे हमें वास्तविक और अनुमानित मानों के बीच का अंतर विज़ुअलाइज़ करने में मदद मिलेगी।
return_rmse फ़ंक्शन test और predicted आर्ग्युमेंट्स लेता है और रूट मीन स्क्वायर एरर (rmse) मेट्रिक प्रदर्शित करता है।
def plot_predictions(test, predicted):
plt.plot(test, color="gray", label="Real")
plt.plot(predicted, color="red", label="Predicted")
plt.title("MasterCard Stock Price Prediction")
plt.xlabel("Time")
plt.ylabel("MasterCard Stock Price")
plt.legend()
plt.show()
def return_rmse(test, predicted):
rmse = np.sqrt(mean_squared_error(test, predicted))
print("The root mean squared error is {:.2f}.".format(rmse))
नीचे दिए लाइन प्लॉट के अनुसार, सिंगल-लेयर LSTM मॉडल ने अच्छा प्रदर्शन किया है।
plot_predictions(test_set,predicted_stock_price)

परिणाम उत्साहजनक हैं क्योंकि मॉडल को टेस्ट डेटासेट पर 6.70 rmse प्राप्त हुआ।
return_rmse(test_set,predicted_stock_price)
>>> The root mean squared error is 6.70.
हम बाकी सब कुछ समान रखेंगे और परिणामों की सही तुलना के लिए केवल LSTM लेयर को GRU लेयर से बदल देंगे। मॉडल संरचना में 125 यूनिट्स वाली एकल GRU लेयर और एक आउटपुट लेयर शामिल है।
model_gru = Sequential()
model_gru.add(GRU(units=125, activation="tanh", input_shape=(n_steps, features)))
model_gru.add(Dense(units=1))
# Compiling the RNN
model_gru.compile(optimizer="RMSprop", loss="mse")
model_gru.summary()
Model: "sequential_5"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
gru_4 (GRU) (None, 125) 48000
_________________________________________________________________
dense_5 (Dense) (None, 1) 126
=================================================================
Total params: 48,126
Trainable params: 48,126
Non-trainable params: 0
_________________________________________________________________
मॉडल 50 epochs और 32 के batch size के साथ सफलतापूर्वक प्रशिक्षित किया गया है।
model_gru.fit(X_train, y_train, epochs=50, batch_size=32)
Epoch 50/50
38/38 [==============================] - 1s 29ms/step - loss: 2.6691e-04
जैसा कि हम देखते हैं, वास्तविक और पूर्वानुमानित मान आपस में काफ़ी क़रीब हैं। पूर्वानुमानित लाइन चार्ट लगभग वास्तविक मानों पर फिट होता है।
GRU_predicted_stock_price = model_gru.predict(X_test)
GRU_predicted_stock_price = sc.inverse_transform(GRU_predicted_stock_price)
plot_predictions(test_set, GRU_predicted_stock_price)

GRU मॉडल ने टेस्ट डेटासेट पर 5.50 का RMSE प्राप्त किया, जो LSTM मॉडल से बेहतर है।
return_rmse(test_set,GRU_predicted_stock_price)
>>> The root mean squared error is 5.50.
दुनिया हाइब्रिड समाधान की ओर बढ़ रही है, जहाँ डेटा साइंटिस्ट इमेज कैप्शनिंग, इमोशन डिटेक्शन, वीडियो सबटाइटलिंग और DNA सीक्वेंसिंग जैसे क्षेत्रों में CNN-RNN हाइब्रिड नेटवर्क का उपयोग कर रहे हैं। हाइब्रिड नेटवर्क मॉडल को दृश्य और समय-क्रम दोनों विशेषताएँ प्रदान करते हैं। RNN के बारे में और जानने के लिए यह कोर्स करें: Recurrent Neural Networks for Language Modeling in Python.
ट्यूटोरियल का पहला भाग रिकरंट न्यूरल नेटवर्क्स की बुनियादी बातें, उनकी सीमाएँ और उन्नत आर्किटेक्चर के रूप में समाधान को कवर करता है। दूसरा भाग LSTM और GRU मॉडलों का उपयोग करके MasterCard के स्टॉक प्राइस पूर्वानुमान विकसित करने पर केंद्रित है। परिणाम स्पष्ट रूप से दिखाते हैं कि समान संरचना और हाइपरपैरामीटर्स होने पर GRU मॉडल ने LSTM से बेहतर प्रदर्शन किया।
यह प्रोजेक्ट DataCamp workspace पर उपलब्ध है।