Skip to content

Commit

Permalink
docs: describe new access syntax (#451)
Browse files Browse the repository at this point in the history
  • Loading branch information
JohannesMessner authored Jul 29, 2022
1 parent cebaf75 commit ed7e9b6
Show file tree
Hide file tree
Showing 9 changed files with 843 additions and 115 deletions.
1 change: 1 addition & 0 deletions docs/datatypes/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ video/index
audio/index
mesh/index
tabular/index
multimodal/index
```
Binary file added docs/datatypes/multimodal/apple.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
640 changes: 640 additions & 0 deletions docs/datatypes/multimodal/index.md

Large diffs are not rendered by default.

179 changes: 124 additions & 55 deletions docs/fundamentals/dataclass/access.md
Original file line number Diff line number Diff line change
@@ -1,87 +1,156 @@
# Access Modality

```{tip}
It is strongly recommended to go through the {ref}`access-elements` section first before continuing.
After {ref}`constructing a multi-modal Document or DocumentArray <mm-construct>`, you can directly access your custom-defined
modalities by their names.

```{admonition} Return types
:class: seealso
Accessing a modality always returns a Document or a DocumentArray, instead of directly returning the data stored in them.
This ensures maximum flexibility for the use.
If you want to learn more about the rationale behind this design, you can read our [blog post](https://medium.com/jina-ai/the-next-level-of-multi-modality-in-docarray-and-jina-a97b38280ab0).
```

Accessing modality means accessing the sub-Documents corresponding to a dataclass field.
(mm-access-doc)=
## Document level access

In the last chapter, we learned how to represent a multimodal document via `@dataclass` and type annotation from `docarray.typing`. We also learned that a multimodal dataclass can be converted into a `Document` object easily. That means if we have a list of multimodal dataclass objects, we can build a DocumentArray out of them:
Even after conversion to {class}`~docarray.document.Document`, custom-defines modalities can be accessed by their names, returning a
{class}`~docarray.document.Document` or, for list-types, a {class}`~docarray.array.document.DocumentArray`.

```python
from docarray import Document, dataclass, DocumentArray
from docarray import Document, dataclass
from typing import List
from docarray.typing import Image, Text


@dataclass
class MMDoc:
banner: Image
description: Text
paragraphs: List[Text]


da = DocumentArray(
[
Document(
MMDoc(banner='test-1.jpeg', description='this is a test white-noise image')
),
Document(
MMDoc(banner='test-2.jpeg', description='another test image but in black')
),
]
doc = Document(
MMDoc(
banner='test.jpg',
paragraphs=['This is a paragraph', 'this is another paragraph'],
)
)

da.summary()
print(doc.banner) # returns a Document with the test.jpg image tensor
print(doc.banner.tensor) # returns the image tensor
print(doc.paragraphs) # returns a DocumentArray with one Document per paragraph
print(doc.paragraphs.texts) # returns the paragraph texts
```


```text
╭────────────── Documents Summary ───────────────╮
│ │
│ Length 2 │
│ Homogenous Documents True │
│ Has nested Documents in ('chunks',) │
│ Common Attributes ('id', 'chunks') │
│ Multimodal dataclass True │
│ │
╰────────────────────────────────────────────────╯
╭──────────────────────── Attributes Summary ────────────────────────╮
│ │
│ Attribute Data type #Unique values Has empty value │
│ ──────────────────────────────────────────────────────────────── │
│ chunks ('ChunkArray',) 2 False │
│ id ('str',) 2 False │
│ │
╰────────────────────────────────────────────────────────────────────╯
<Document ('id', 'parent_id', 'granularity', 'tensor', 'mime_type', 'uri', '_metadata', 'modality') at eaccc9c573c07f13b7ee8aa04a83c9eb>
[[[255 255 255]
[255 255 255]
[255 255 255]]]
<DocumentArray (length=2) at 140540453339296>
['This is a paragraph', 'this is another paragraph']
```

The returned Documents (or DocumentArrays) can be directly used to store additional information about the modality:

```python
import torch, torchvision

model = torchvision.models.resnet50(pretrained=True)
banner_tensor = torch.tensor(doc.banner.tensor).transpose(0, 2).unsqueeze(0)
doc.banner.embedding = model(banner_tensor)
```

### Select nested fields

Nested field, coming from {ref}`nested dataclasses <mm-annotation>`, can be accessed by selecting the outer field,
and then selecting the inner field:

```python
from docarray import dataclass, Document
from docarray.typing import Image, Text


@dataclass
class InnerDoc:
description: Text
banner: Image = 'test-1.jpeg'


@dataclass
class OuterDoc:
feature_image: InnerDoc
website: str = 'https://jina.ai'


doc = Document(OuterDoc(feature_image=InnerDoc(description='this is a description')))
print(
doc.feature_image.description
) # returns a Document with 'this is a description' as text
print(doc.feature_image.description.text) # returns 'this is a description'
```

A natural question would be, how do we select those Documents that correspond to `MMDoc.banner`?
```text
<Document ('id', 'parent_id', 'granularity', 'text', 'modality') at 94de1bef2fc8010ff4fe86791a671c44>
this is a description
```

This chapter describes how to select the sub-documents that correspond to a modality from a DocumentArray. So let me reiterate the logic here: when calling `Document()` to build Document object from a dataclass object, each field in that dataclass will generate a sub-document nested under `.chunks` or even `.chunks.chunks.chunks` at arbitrary level (except primitive types, which are stored in the `tags` of the root Document). To process a dataclass field via existing DocArray API/Jina/Hub Executor, we need a way to accurately select those sub-documents from the nested structure, which is the purpose of this chapter.
(mm-access-da)=
## DocumentArray level access

## Selector Syntax
Custom modalities can be accessed through the familiar {ref}`selector syntax <access-elements>`.

Following the syntax convention described in {ref}`access-elements`, a modality selector also starts with `@`, it uses `.` to indicate the field of the dataclass. Selecting a DocumentArray always results in another DocumentArray.
Like all selectors, a selector for a multi-modal attribute begins with `@`.
The fact that a custom modality is accessed is denoted through the addition of a `.`, coming before a list of modality names:

```text
@.[field1, field2, ...]
^^ ~~~~~~ ~~~~~~
|| | |
|| |-------|
|| |
|| | --- indicate the field of dataclass
|| | --- indicate the field of dataclass (modality name)
||
|| ------ indicate the start of modality selector
|
| ---- indicate the start of selector
```

Use the above DocumentArray as an example,
Selecting a modality form a DocumentArray always results in another DocumentArray:

```python
from docarray import Document, dataclass, DocumentArray
from docarray.typing import Image, Text


@dataclass
class MMDoc:
banner: Image
description: Text

````{tab} Select Documents corresponding to .banner

da = DocumentArray(
[
Document(
MMDoc(banner='test-1.jpeg', description='this is a test white-noise image')
),
Document(
MMDoc(banner='test-2.jpeg', description='another test image but in black')
),
]
)

da.summary()
```

`````{tab} Select Documents corresponding to .banner
```python
da['@.[banner]']
```
````{dropdown} Output
```text
╭───────────────────────────── Documents Summary ──────────────────────────────╮
│ │
Expand All @@ -107,15 +176,19 @@ da['@.[banner]']
╰───────────────────────────────────────────────────────────────────╯
```
````
````{tab} Select Documents corresponding to .description
`````

`````{tab} Select Documents corresponding to .description
```python
da['@.[description]']
```
````{dropdown} Output
```text
╭───────────────────────────── Documents Summary ──────────────────────────────╮
│ │
Expand All @@ -139,21 +212,19 @@ da['@.[description]']
╰────────────────────────────────────────────────────────────────╯
```
````
### Select multiple fields
`````

You can select multiple fields by including them in the square brackets, separated by a comma `,`.
### Select multiple fields

````{tab} Select Documents correspond to two fields
You can select multiple fields by including them in the square brackets, separated by a comma `,`:

```python
da['@.[description, banner]']
```
````

````{tab} Result
````{dropdown} Output
```text
Expand Down Expand Up @@ -187,25 +258,23 @@ da['@.[description, banner]']

### Slice dataclass objects

Remember each dataclass object corresponds to one Document object, you can first slice the DocumentArray before selecting the field. Specifically, you can do
Remember each dataclass object corresponds to one Document object, you can first slice the DocumentArray before selecting the field. Specifically, you can do:

```text
@r[slice].[field1, field2, ...]
```

where `slice` can be any slice syntax accepted in {ref}`access-elements`.

For example, to select the sub-Document `.banner` for only the first Document,

````{tab} Select .banner of the first dataclass
For example, to select the sub-Document `.banner` for only the first Document:


```python
da['@r[:1].[banner]']
```

````

````{tab} Result
````{dropdown} Output
```text
╭───────────────────────────── Documents Summary ──────────────────────────────╮
Expand Down
28 changes: 25 additions & 3 deletions docs/fundamentals/dataclass/construct.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
(mm-construct)=
# Construct

```{tip}
Expand All @@ -21,7 +22,20 @@ class MyMultiModalDoc:
m = MyMultiModalDoc(avatar='test-1.jpeg', description='hello, world')
```

**Each field is a modality.** The above example contains two modalities: image and text.
**Each field is a modality.** The above example contains two modalities: image and text.

```{Caution}
Be careful when assigning names to your modalities.
Do not use names that are properties of {class}`~docarray.document.Document`, such as
`text`, `tensor`, `embedding`, etc.
Instead, use more specific names that fit your domain, such as `avatar` and `description` in the example above.
If there is a conflict between the name of a modality and a property of {class}`~docarray.document.Document`,
no guarantees about the behavior while {ref}`accessing <mm-access-doc>` such a name can be made.
```

To convert it into a `Document` object, simply:

Expand All @@ -32,7 +46,9 @@ d = Document(m)
d.summary()
```

One can see that this creates a Document object with two chunks nested.
This creates a Document object with two chunks:

````{dropdown} Nested structure (chunks)
```text
📄 Document: f3b193bbe8403c3ce1599b82f941f68a
Expand All @@ -59,7 +75,9 @@ One can see that this creates a Document object with two chunks nested.
╰──────────────────────┴───────────────────────────────────────────────────────╯
```
To convert a Document object back to a `MyMultiModalDoc` object,
````

To convert a Document object back to a `MyMultiModalDoc` object, do:

```python
m = MyMultiModalDoc(d)
Expand Down Expand Up @@ -162,6 +180,7 @@ d = Document(m)

One can look at the structure of `d` via `d.summary()`:

````{dropdown} Nested structure (chunks)
```text
📄 Document: 90c744c5155c2356d27f8c91955f70f7
Expand Down Expand Up @@ -197,6 +216,9 @@ One can look at the structure of `d` via `d.summary()`:
╰──────────────┴───────────────────────────────────────────────────────────────╯
```
````

(mm-annotation)=
## Behavior of field annotation

This section explains the behavior of field annotations in details.
Expand Down
Loading

0 comments on commit ed7e9b6

Please sign in to comment.