1. Giới thiệu
Những tiến bộ gần đây trong học sâu đã giúp chúng ta có thể biểu diễn văn bản và dữ liệu khác theo cách nắm bắt được ý nghĩa ngữ nghĩa. Điều này đã dẫn đến một phương pháp tìm kiếm mới, được gọi là tìm kiếm dựa trên vectơ. Phương pháp này sử dụng các biểu diễn vectơ của văn bản (còn gọi là vectơ nhúng) để tìm những tài liệu phù hợp nhất với cụm từ tìm kiếm của người dùng. Tìm kiếm vectơ được ưu tiên hơn tìm kiếm truyền thống cho các ứng dụng như tìm kiếm quần áo, nơi người dùng thường tìm kiếm các mặt hàng theo nội dung mô tả, kiểu dáng hoặc bối cảnh thay vì theo tên sản phẩm hoặc thương hiệu chính xác. Chúng ta có thể tích hợp cơ sở dữ liệu Cloud Spanner với Vector Search để thực hiện việc so khớp mức độ tương đồng của vectơ. Bằng cách sử dụng Spanner và Vector Search cùng nhau, khách hàng có thể tạo ra một giải pháp tích hợp mạnh mẽ, kết hợp tính sẵn có, độ tin cậy và quy mô của Spanner với các chức năng tìm kiếm nâng cao về mức độ tương đồng của Vertex AI Vector Search. Hoạt động tìm kiếm này được thực hiện bằng cách so sánh embeddings của các mục trong chỉ mục của Tìm kiếm vectơ và trả về các kết quả trùng khớp tương tự nhất.
Trường hợp sử dụng
Hãy tưởng tượng bạn là một nhà khoa học dữ liệu tại một nhà bán lẻ thời trang, đang cố gắng bắt kịp các xu hướng, lượt tìm kiếm sản phẩm và đề xuất thay đổi nhanh chóng. Thách thức là bạn có nguồn lực hạn chế và các yếu tố ngăn cách dữ liệu. Bài đăng này trên blog minh hoạ cách triển khai trường hợp sử dụng đề xuất quần áo bằng cách sử dụng phương pháp tìm kiếm tương tự trên dữ liệu quần áo.Các bước sau đây sẽ được đề cập:
- Dữ liệu lấy từ Spanner
- Các vectơ được tạo cho dữ liệu quần áo bằng cách sử dụng ML.PREDICT và được lưu trữ trong Spanner
- Dữ liệu vectơ Spanner được tích hợp với tính năng Tìm kiếm vectơ bằng cách sử dụng các công việc quy trình và luồng dữ liệu
- Tính năng Tìm kiếm bằng vectơ được thực hiện để tìm kết quả khớp tương tự cho nội dung đầu vào mà người dùng đã nhập
Chúng ta sẽ tạo một ứng dụng web bản minh họa để thực hiện tìm kiếm quần áo dựa trên hoạt động đầu vào của người dùng. Ứng dụng này cho phép người dùng tìm kiếm quần áo bằng cách nhập nội dung mô tả bằng văn bản.
Spanner đến Chỉ mục tìm kiếm vectơ:
Dữ liệu cho tính năng tìm kiếm quần áo được lưu trữ trong Spanner. Chúng ta sẽ gọi API Embeddings của Vertex AI trong cấu trúc ML.PREDICT trực tiếp từ dữ liệu Spanner. Sau đó, chúng ta sẽ tận dụng các công việc Dataflow và Workflow để tải hàng loạt dữ liệu này (kho hàng và mô hình nhúng) lên Vertex AI Vector Search và làm mới chỉ mục.
Chạy truy vấn của người dùng trên chỉ mục:
Khi người dùng nhập nội dung mô tả về quần áo, ứng dụng sẽ tạo các mục nhúng theo thời gian thực bằng Text Embeddings API. Sau đó, thông tin này được gửi dưới dạng dữ liệu đầu vào cho Vector Search API để tìm 10 nội dung mô tả sản phẩm có liên quan trong chỉ mục và hiển thị hình ảnh tương ứng.
Tổng quan về Cấu trúc (Architecture)
Cấu trúc của ứng dụng Tìm kiếm vectơ Spanner được thể hiện trong sơ đồ gồm 2 phần sau:
Spanner to Vector Search Index: 
Ứng dụng khách để chạy các truy vấn của người dùng trên chỉ mục:
Sản phẩm bạn sẽ tạo ra
Spanner đến Chỉ mục vectơ:
- Cơ sở dữ liệu Spanner để lưu trữ và quản lý dữ liệu nguồn cũng như các mục nhúng tương ứng
- Một công việc trong quy trình giúp tải hàng loạt dữ liệu (mã nhận dạng và các mục nhúng) lên cơ sở dữ liệu Vertex AI Vector Search.
- Vector Search API được dùng để tìm nội dung mô tả sản phẩm có liên quan trong chỉ mục.
Chạy truy vấn của người dùng trên chỉ mục:
- Một ứng dụng web cho phép người dùng nhập nội dung mô tả bằng văn bản về quần áo, thực hiện tìm kiếm tương tự bằng cách sử dụng điểm cuối chỉ mục đã triển khai và trả về những bộ quần áo gần nhất với nội dung đầu vào.
Cách hoạt động
Khi người dùng nhập nội dung mô tả bằng văn bản về quần áo, ứng dụng web sẽ gửi nội dung mô tả đó đến Vector Search API. Sau đó, Vector Search API sẽ sử dụng các mục nhúng của nội dung mô tả quần áo để tìm nội dung mô tả sản phẩm phù hợp nhất trong chỉ mục. Sau đó, nội dung mô tả sản phẩm và hình ảnh tương ứng sẽ được hiển thị cho người dùng. Quy trình chung như sau:
- Tạo các mục nhúng cho dữ liệu được lưu trữ trong Spanner.
- Xuất và tải các mục nhúng lên chỉ mục của Tìm kiếm vectơ.
- Truy vấn chỉ mục của Tìm kiếm vectơ để tìm các mục tương tự bằng cách thực hiện tìm kiếm lân cận nhất.
2. Yêu cầu
- Một trình duyệt, chẳng hạn như Chrome hoặc Firefox
- Một dự án trên Google Cloud đã bật tính năng thanh toán
Trước khi bắt đầu
- Trong Google Cloud Console, trên trang chọn dự án, hãy chọn hoặc tạo một dự án trên Google Cloud
- Đảm bảo rằng bạn đã bật tính năng thanh toán cho dự án trên đám mây của bạn. Tìm hiểu cách kiểm tra xem tính năng thanh toán có được bật trên một dự án hay không
- Đảm bảo bạn đã bật tất cả các API cần thiết (Cloud Spanner, Vertex AI, Google Cloud Storage)
- Bạn sẽ sử dụng Cloud Shell, một môi trường dòng lệnh chạy trong Google Cloud và được tải sẵn gcloud. Tham khảo tài liệu để biết các lệnh và cách sử dụng gcloud. Nếu bạn chưa đặt dự án, hãy dùng lệnh sau để đặt:
gcloud config set project <YOUR_PROJECT_ID>
- Chuyển đến trang Cloud Spanner bằng dự án Google Cloud đang hoạt động để bắt đầu
3. Phần phụ trợ: Tạo nguồn dữ liệu và các mục nhúng Spanner
Trong trường hợp sử dụng này, cơ sở dữ liệu Spanner lưu trữ kho hàng quần áo cùng với hình ảnh và nội dung mô tả tương ứng. Đảm bảo bạn tạo các mục nhúng cho nội dung mô tả bằng văn bản và lưu trữ chúng trong cơ sở dữ liệu Spanner dưới dạng ARRAY<float64>.
- Tạo dữ liệu Spanner
Tạo một thực thể có tên là "spanner-vertex" và một cơ sở dữ liệu có tên là "spanner-vertex-embeddings". Tạo bảng bằng DDL:
CREATE TABLE
apparels ( id NUMERIC,
category STRING(100),
sub_category STRING(50),
uri STRING(200),
content STRING(2000),
embedding ARRAY<FLOAT64>
)
PRIMARY KEY
(id);
- Chèn dữ liệu vào bảng bằng cách sử dụng SQL INSERT
Bạn có thể xem các tập lệnh chèn cho dữ liệu mẫu tại đây.
- Tạo mô hình Nhúng văn bản
Đây là yêu cầu bắt buộc để chúng tôi có thể tạo các vectơ nhúng cho nội dung trong dữ liệu đầu vào. Dưới đây là DDL cho bảng tương tự:
CREATE MODEL text_embeddings INPUT(content STRING(MAX))
OUTPUT(
embeddings
STRUCT<
statistics STRUCT<truncated BOOL, token_count FLOAT64>,
values ARRAY<FLOAT64>>
)
REMOTE OPTIONS (
endpoint = '//aiplatform.googleapis.com/projects/abis-345004/locations/us-central1/publishers/google/models/textembedding-gecko');
- Tạo các vectơ nhúng văn bản cho dữ liệu nguồn
Tạo một bảng để lưu trữ các mục nhúng và chèn các mục nhúng đã tạo. Trong một ứng dụng cơ sở dữ liệu thực tế, việc tải dữ liệu lên Spanner cho đến bước 2 sẽ mang tính giao dịch. Để giữ nguyên các phương pháp hay nhất về thiết kế, tôi muốn giữ cho các bảng giao dịch được chuẩn hoá, vì vậy, tôi sẽ tạo một bảng riêng cho các mục nhúng.
CREATE TABLE apparels_embeddings (id string(100), embedding ARRAY<FLOAT64>)
PRIMARY KEY (id);
INSERT INTO apparels_embeddings(id, embeddings)
SELECT CAST(id as string), embeddings.values
FROM ML.PREDICT(
MODEL text_embeddings,
(SELECT id, content from apparels)
) ;
Bây giờ, khi nội dung và các mục nhúng hàng loạt đã sẵn sàng, hãy tạo một Chỉ mục tìm kiếm vectơ và Điểm cuối để lưu trữ các mục nhúng sẽ giúp thực hiện Tìm kiếm vectơ.
4. Tác vụ trong quy trình công việc: Xuất dữ liệu Spanner sang Vector Search
- Tạo bộ chứa Cloud Storage
Bạn cần phải lưu trữ các mục nhúng từ Spanner trong một vùng chứa GCS ở định dạng json mà Vector Search dự kiến sẽ nhận được dưới dạng dữ liệu đầu vào. Tạo một vùng chứa trong cùng khu vực với dữ liệu của bạn trong Spanner. Tạo một thư mục bên trong nếu cần, nhưng chủ yếu là tạo một tệp trống có tên là empty.json trong thư mục đó.
- Thiết lập quy trình làm việc trên đám mây
Cách thiết lập một quy trình xuất hàng loạt từ Spanner sang chỉ mục Tìm kiếm vectơ của Vertex AI:
Tạo một chỉ mục trống:
Đảm bảo rằng Chỉ mục tìm kiếm vectơ nằm trong cùng khu vực với Nhóm Cloud Storage và dữ liệu của bạn. Làm theo 11 bước hướng dẫn trong thẻ bảng điều khiển ở phần Tạo chỉ mục để cập nhật theo lô trong trang quản lý chỉ mục. Trong thư mục được truyền đến contentsDeltaUri, hãy tạo một tệp trống có tên là empty.json vì bạn sẽ không thể tạo chỉ mục nếu không có tệp này. Thao tác này sẽ tạo một chỉ mục trống.
Nếu đã có chỉ mục, bạn có thể bỏ qua bước này. Quy trình này sẽ ghi đè chỉ mục của bạn.
Lưu ý: Bạn không thể triển khai một chỉ mục trống vào một điểm cuối. Vì vậy, chúng ta sẽ hoãn bước triển khai đến một điểm cuối sang bước sau, sau khi xuất dữ liệu vectơ sang Cloud Storage.
Sao chép kho lưu trữ git này: Có nhiều cách để sao chép một kho lưu trữ git, một cách là chạy lệnh sau bằng GitHub CLI. Chạy 2 lệnh bên dưới qua Cloud Shell Terminal:
gh repo clone cloudspannerecosystem/spanner-ai
cd spanner-ai/vertex-vector-search/workflows
Thư mục này chứa 2 tệp
batch-export.yaml: Đây là định nghĩa quy trình công việc.sample-batch-input.json: Đây là mẫu tham số đầu vào của quy trình.
Thiết lập input.json từ tệp mẫu: Trước tiên, hãy sao chép tệp json mẫu.
cp sample-batch-input.json input.json
Sau đó, hãy chỉnh sửa input.json bằng thông tin chi tiết cho dự án của bạn. Trong trường hợp này, tệp json của bạn sẽ có dạng như sau:
{
"project_id": "<<YOUR_PROJECT>>",
"location": "<<us-central1>>",
"dataflow": {
"temp_location": "gs://<<YOUR_BUCKET>>/<<FOLDER_IF_ANY>>/workflow_temp"
},
"gcs": {
"output_folder": "gs://<<YOUR_BUCKET>>/<<FOLDER_IF_ANY>>/workflow_output"
},
"spanner": {
"instance_id": "spanner-vertex",
"database_id": "spanner-vertex-embeddings",
"table_name": "apparels_embeddings",
"columns_to_export": "embedding,id"
},
"vertex": {
"vector_search_index_id": "<<YOUR_INDEX_ID>>"
}
}
Thiết lập quyền
Đối với môi trường sản xuất, bạn nên tạo một tài khoản dịch vụ mới và cấp cho tài khoản đó một hoặc nhiều vai trò IAM có chứa các quyền tối thiểu cần thiết để quản lý dịch vụ. Bạn cần có các vai trò sau để thiết lập quy trình xuất dữ liệu từ Spanner (embeddings) sang chỉ mục của Tìm kiếm vectơ:
Tài khoản dịch vụ Cloud Workflow:
Theo mặc định, tài khoản này sử dụng tài khoản dịch vụ mặc định của Compute Engine.
Nếu sử dụng tài khoản dịch vụ được định cấu hình theo cách thủ công, bạn phải thêm các vai trò sau:
Để kích hoạt một công việc Dataflow: Quản trị viên Dataflow, Worker Dataflow.
Cách mạo danh tài khoản dịch vụ của worker quy trình dữ liệu: Người dùng tài khoản dịch vụ.
Để ghi Nhật ký: Người ghi nhật ký.
Cách kích hoạt quá trình tạo lại Vertex AI Vector Search: Người dùng Vertex AI
Tài khoản dịch vụ Dataflow Worker:
Nếu sử dụng tài khoản dịch vụ được định cấu hình theo cách thủ công, bạn phải thêm các vai trò sau:
Để quản lý luồng dữ liệu: Quản trị viên Dataflow, Nhân viên Dataflow. Để đọc dữ liệu từ Spanner: Trình đọc cơ sở dữ liệu Cloud Spanner. Quyền ghi đối với GCS Container Registry đã chọn: Chủ sở hữu bộ chứa lưu trữ GCS.
- Triển khai quy trình công việc trên đám mây
Triển khai tệp yaml quy trình công việc vào dự án trên đám mây của bạn. Bạn có thể định cấu hình khu vực hoặc vị trí mà quy trình công việc sẽ chạy khi được thực thi.
gcloud workflows deploy vector-export-workflow --source=batch-export.yaml --location="us-central1" [--service account=<service_account>]
or
gcloud workflows deploy vector-export-workflow --source=batch-export.yaml --location="us-central1"
Giờ đây, quy trình công việc sẽ xuất hiện trên trang Quy trình công việc trong bảng điều khiển Google Cloud.
Lưu ý: Bạn cũng có thể tạo và triển khai quy trình công việc từ bảng điều khiển Cloud. Làm theo lời nhắc trong bảng điều khiển Cloud. Đối với định nghĩa quy trình công việc, hãy sao chép và dán nội dung của batch-export.yaml.
Sau khi hoàn tất, hãy thực thi quy trình để bắt đầu xuất dữ liệu.
- Thực thi quy trình công việc trên đám mây
Chạy lệnh sau để thực thi quy trình:
gcloud workflows execute vector-export-workflow --data="$(cat input.json)"
Lần thực thi sẽ xuất hiện trong thẻ Thực thi trong phần Quy trình công việc. Thao tác này sẽ tải dữ liệu của bạn vào cơ sở dữ liệu Tìm kiếm vectơ và lập chỉ mục dữ liệu đó.
Lưu ý: Bạn cũng có thể thực thi từ bảng điều khiển bằng cách sử dụng nút Thực thi. Làm theo lời nhắc và đối với dữ liệu đầu vào, hãy sao chép và dán nội dung của input.json tuỳ chỉnh.
5. Triển khai chỉ mục Tìm kiếm vectơ
Triển khai chỉ mục đến một điểm cuối
Bạn có thể làm theo các bước dưới đây để triển khai chỉ mục:
- Trên trang Chỉ mục tìm kiếm vectơ, bạn sẽ thấy nút TRIỂN KHAI bên cạnh chỉ mục mà bạn vừa tạo ở bước 2 của phần trước. Hoặc bạn có thể chuyển đến trang thông tin chỉ mục rồi nhấp vào nút DEPLOY TO ENDPOINT (TRIỂN KHAI ĐẾN ĐIỂM CUỐI).
- Cung cấp thông tin cần thiết và triển khai chỉ mục đến một điểm cuối.
Ngoài ra, bạn có thể xem sổ tay này để triển khai sổ tay đó đến một điểm cuối (chuyển đến phần triển khai của sổ tay). Sau khi triển khai, hãy ghi lại mã chỉ mục và URL điểm cuối đã triển khai.
6. Giao diện người dùng: Dữ liệu người dùng cho Vector Search
Hãy tạo một ứng dụng Python đơn giản có UX dựa trên gradio để nhanh chóng kiểm thử việc triển khai của chúng ta: Bạn có thể tham khảo cách triển khai tại đây để triển khai ứng dụng minh hoạ này trong sổ tay colab của riêng bạn.
- Chúng ta sẽ sử dụng aiplatform python SDK để gọi Embeddings API và cũng để gọi điểm cuối chỉ mục của Tìm kiếm Vector.
# [START aiplatform_sdk_embedding]
!pip install google-cloud-aiplatform==1.35.0 --upgrade --quiet --user
import vertexai
vertexai.init(project=PROJECT_ID, location="us-central1")
from vertexai.language_models import TextEmbeddingModel
import sys
if "google.colab" in sys.modules:
# Define project information
PROJECT_ID = " " # Your project id
LOCATION = " " # Your location
# Authenticate user to Google Cloud
from google.colab import auth
auth.authenticate_user()
- Chúng ta sẽ sử dụng gradio để minh hoạ ứng dụng AI mà chúng ta đang xây dựng một cách nhanh chóng và dễ dàng bằng giao diện người dùng. Khởi động lại thời gian chạy trước khi bạn triển khai bước này.
!pip install gradio
import gradio as gr
- Từ ứng dụng web khi người dùng nhập dữ liệu, hãy gọi Embeddings API. Chúng tôi sẽ sử dụng mô hình nhúng văn bản: textembedding-gecko@latest
Phương thức dưới đây sẽ gọi Mô hình nhúng văn bản và trả về các vectơ nhúng cho văn bản do người dùng nhập:
def text_embedding(content) -> list:
"""Text embedding with a Large Language Model."""
model = TextEmbeddingModel.from_pretrained("textembedding-gecko@latest")
embeddings = model.get_embeddings(content)
for embedding in embeddings:
vector = embedding.values
#print(f"Length of Embedding Vector: {len(vector)}")
return vector
Kiểm thử
text_embedding("red shorts for girls")
Bạn sẽ thấy một kết quả tương tự như bên dưới (xin lưu ý rằng hình ảnh bị cắt theo chiều cao nên bạn không thể thấy toàn bộ phản hồi vectơ):

- Khai báo mã chỉ mục đã triển khai và mã điểm cuối
from google.cloud import aiplatform
DEPLOYED_INDEX_ID = "spanner_vector1_1702366982123"
#Vector Search Endpoint
index_endpoint = aiplatform.MatchingEngineIndexEndpoint('projects/273845608377/locations/us-central1/indexEndpoints/2021628049526620160')
- Xác định phương thức Tìm kiếm vectơ để gọi điểm cuối chỉ mục và cho thấy kết quả với 10 kết quả trùng khớp gần nhất cho phản hồi nhúng tương ứng với hoạt động đầu vào của người dùng.
Trong định nghĩa phương thức bên dưới cho Tìm kiếm vectơ, hãy lưu ý rằng phương thức find_neighbors được gọi để xác định 10 vectơ gần nhất.
def vector_search(content) -> list:
result = text_embedding(content)
#call_vector_search_api(content)
index_endpoint = aiplatform.MatchingEngineIndexEndpoint('projects/273845608377/locations/us-central1/indexEndpoints/2021628049526620160')
# run query
response = index_endpoint.find_neighbors(
deployed_index_id = DEPLOYED_INDEX_ID,
queries = [result],
num_neighbors = 10
)
out = []
# show the results
for idx, neighbor in enumerate(response[0]):
print(f"{neighbor.distance:.2f} {spanner_read_data(neighbor.id)}")
out.append(f"{spanner_read_data(neighbor.id)}")
return out
Bạn cũng sẽ nhận thấy lệnh gọi đến phương thức spanner_read_data. Hãy xem xét vấn đề này ở bước tiếp theo.
- Xác định việc triển khai phương thức đọc dữ liệu Spanner, phương thức này sẽ gọi phương thức execute_sql để trích xuất những hình ảnh tương ứng với mã nhận dạng của các vectơ lân cận gần nhất được trả về từ bước cuối cùng.
!pip install google-cloud-spanner==3.36.0
from google.cloud import spanner
instance_id = "spanner-vertex"
database_id = "spanner-vertex-embeddings"
projectId = PROJECT_ID
client = spanner.Client()
client.project = projectId
instance = client.instance(instance_id)
database = instance.database(database_id)
def spanner_read_data(id):
query = "SELECT uri FROM apparels where id = " + id
outputs = []
with database.snapshot() as snapshot:
results = snapshot.execute_sql(query)
for row in results:
#print(row)
#output = "ID: {}, CONTENT: {}, URI: {}".format(*row)
output = "{}".format(*row)
outputs.append(output)
return "\n".join(outputs)
Thao tác này sẽ trả về URL của những hình ảnh tương ứng với các vectơ đã chọn.
- Cuối cùng, hãy kết hợp các phần này trong giao diện người dùng và kích hoạt quy trình Tìm kiếm vectơ
from PIL import Image
def call_search(query):
response = vector_search(query)
return response
input_text = gr.Textbox(label="Enter your query. Examples: Girls Tops White Casual, Green t-shirt girls, jeans shorts, denim skirt etc.")
output_texts = [gr.Image(label="") for i in range(10)]
demo = gr.Interface(fn=call_search, inputs=input_text, outputs=output_texts, live=True)
resp = demo.launch(share = True)
Bạn sẽ thấy kết quả như minh hoạ dưới đây:

Hình ảnh: Đường liên kết
Xem video kết quả: tại đây.
7. Dọn dẹp
Để tránh bị tính phí vào tài khoản Google Cloud của bạn cho các tài nguyên được dùng trong bài đăng này, hãy làm theo các bước sau:
- Trong bảng điều khiển Google Cloud, hãy chuyển đến trang Quản lý tài nguyên.
- Trong danh sách dự án, hãy chọn dự án mà bạn muốn xoá, rồi nhấp vào Xoá.
- Trong hộp thoại, hãy nhập mã dự án rồi nhấp vào Tắt để xoá dự án.
- Nếu bạn không muốn xoá dự án, hãy xoá phiên bản Spanner bằng cách chuyển đến phiên bản mà bạn vừa tạo cho dự án này rồi nhấp vào nút XOÁ PHIÊN BẢN ở góc trên cùng bên phải của trang tổng quan về phiên bản.
- Bạn cũng có thể chuyển đến chỉ mục Tìm kiếm vectơ, huỷ triển khai điểm cuối và chỉ mục, rồi xoá chỉ mục.
8. Kết luận
Xin chúc mừng! Bạn đã hoàn tất thành công việc triển khai Spanner – Vertex Vector Search bằng cách
- Tạo nguồn dữ liệu và các mục nhúng Spanner cho những ứng dụng có nguồn gốc từ cơ sở dữ liệu Spanner.
- Tạo chỉ mục cơ sở dữ liệu Tìm kiếm vectơ.
- Tích hợp dữ liệu vectơ từ Spanner vào Vector Search bằng cách sử dụng các công việc Dataflow và Workflow.
- Triển khai chỉ mục đến một điểm cuối.
- Cuối cùng, hãy gọi Tìm kiếm vectơ dựa trên hoạt động đầu vào của người dùng trong quá trình triển khai Vertex AI SDK dựa trên Python.
Bạn có thể thoải mái mở rộng việc triển khai cho trường hợp sử dụng của riêng mình hoặc cải thiện trường hợp sử dụng hiện tại bằng các tính năng mới. Tìm hiểu thêm về các khả năng học máy của Spanner tại đây.